package thumbnail import ( "bytes" "image" "image/gif" "image/jpeg" "image/png" "io" "os" "golang.org/x/image/bmp" "golang.org/x/image/webp" ) type imageDecoder func(io.Reader) (image.Image, error) var imageDecoders map[string]imageDecoder = map[string]imageDecoder{ `image/jpeg`: jpeg.Decode, `image/png`: png.Decode, `image/gif`: gif.Decode, `image/webp`: webp.Decode, `image/bmp`: bmp.Decode, } func (this *DirectThumbnailer) renderImageFile(absPath, mimeType string) ([]byte, error) { fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400) if err != nil { return nil, err } defer fh.Close() fn, ok := imageDecoders[mimeType] if !ok { return nil, ErrUnsupportedFiletype } src, err := fn(fh) if err != nil { return nil, err } return this.RenderScaledImage(src) } func (this *DirectThumbnailer) RenderScaledImage(src image.Image) ([]byte, error) { srcW := src.Bounds().Max.X srcH := src.Bounds().Max.Y destW := 0 destH := 0 if srcW > srcH { destW = this.cfg.Width destH = this.cfg.Height * srcH / srcW } else { destW = this.cfg.Width * srcW / srcH destH = this.cfg.Height } offsetX := (this.cfg.Width - destW) / 2 offsetY := (this.cfg.Height - destH) / 2 scaleW := float64(srcW) / float64(destW) scaleH := float64(srcH) / float64(destH) dest := image.NewRGBA(image.Rectangle{Max: image.Point{X: this.cfg.Width, Y: this.cfg.Height}}) switch this.cfg.Scale { case SCALEFMT_BILINEAR: for y := 0; y < destH; y += 1 { for x := 0; x < destW; x += 1 { c00 := src.At(int(float64(x)*scaleW), int(float64(y)*scaleH)) c01 := src.At(int((float64(x)+0.5)*scaleW), int(float64(y)*scaleH)) c10 := src.At(int(float64(x)*scaleW), int((float64(y)+0.5)*scaleH)) c11 := src.At(int((float64(x)+0.5)*scaleW), int((float64(y)+0.5)*scaleH)) cBlend := Blend(Blend(c00, c01), Blend(c10, c11)) dest.Set(x+offsetX, y+offsetY, cBlend) } } case SCALEFMT_NN: for y := 0; y < destH; y += 1 { for x := 0; x < destW; x += 1 { mapx := int(float64(x) * scaleW) mapy := int(float64(y) * scaleH) dest.Set(x+offsetX, y+offsetY, src.At(mapx, mapy)) } } } switch this.cfg.Output { case OUTPUT_PNG_CRUSH: return crushFast(dest) case OUTPUT_JPG: buff := bytes.Buffer{} err := jpeg.Encode(&buff, dest, &jpeg.Options{Quality: jpeg.DefaultQuality}) if err != nil { return nil, err } return buff.Bytes(), nil default: return nil, ErrInvalidOption } }