diff --git a/image.go b/image.go index ec0acf3..9e2eb07 100644 --- a/image.go +++ b/image.go @@ -1,10 +1,8 @@ package thumbnail import ( - "bytes" "image" "image/color" - "image/png" ) func Blend(a, b color.Color) color.Color { @@ -80,12 +78,21 @@ func (this *Thumbnailer) RenderScaledImage(src image.Image) ([]byte, error) { } } - var b bytes.Buffer + /*var b bytes.Buffer*/ - err := png.Encode(&b, dest) - if err != nil { - return nil, err - } + return crush(dest, 3) - return b.Bytes(), nil + /* + err := png.Encode(&b, dest) + if err != nil { + return nil, err + } + */ + /* + err := jpeg.Encode(&b, dest, nil) + if err != nil { + return nil, err + } + + return b.Bytes(), nil*/ } diff --git a/imagecrush.go b/imagecrush.go new file mode 100644 index 0000000..f387192 --- /dev/null +++ b/imagecrush.go @@ -0,0 +1,102 @@ +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 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 +}