scaler: support FitInside, Bilinear{Fast/Accurate} variants, BiCubic
This commit is contained in:
parent
525dcf31ad
commit
9a833d025a
@ -50,7 +50,10 @@ func (this *DirectThumbnailer) RenderFile(absPath string) ([]byte, error) {
|
|||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
|
|
||||||
dest := this.scaleImage(src)
|
dest, err := this.scaleImage(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Rasterise result
|
// Rasterise result
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ const (
|
|||||||
FitInside AspectFormat = 82 // Crop to dimensions
|
FitInside AspectFormat = 82 // Crop to dimensions
|
||||||
|
|
||||||
NearestNeighbour ScaleFormat = 120
|
NearestNeighbour ScaleFormat = 120
|
||||||
Bilinear ScaleFormat = 121
|
BilinearFast ScaleFormat = 121
|
||||||
|
BilinearAccurate ScaleFormat = 122
|
||||||
|
Bicubic ScaleFormat = 123
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
77
scaler.go
77
scaler.go
@ -7,13 +7,18 @@ import (
|
|||||||
"image/png"
|
"image/png"
|
||||||
|
|
||||||
"golang.org/x/image/bmp"
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/draw"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (this *DirectThumbnailer) scaleImage(src image.Image) image.Image {
|
func (this *DirectThumbnailer) scaleImage(src image.Image) (image.Image, error) {
|
||||||
srcW := src.Bounds().Max.X
|
srcW := src.Bounds().Max.X
|
||||||
srcH := src.Bounds().Max.Y
|
srcH := src.Bounds().Max.Y
|
||||||
destW := 0
|
var srcCopyPosition, destCopyPosition image.Rectangle
|
||||||
destH := 0
|
|
||||||
|
switch this.cfg.Aspect {
|
||||||
|
case FitInside:
|
||||||
|
|
||||||
|
var destW, destH int
|
||||||
|
|
||||||
if srcW > srcH {
|
if srcW > srcH {
|
||||||
destW = this.cfg.Width
|
destW = this.cfg.Width
|
||||||
@ -26,40 +31,52 @@ func (this *DirectThumbnailer) scaleImage(src image.Image) image.Image {
|
|||||||
offsetX := (this.cfg.Width - destW) / 2
|
offsetX := (this.cfg.Width - destW) / 2
|
||||||
offsetY := (this.cfg.Height - destH) / 2
|
offsetY := (this.cfg.Height - destH) / 2
|
||||||
|
|
||||||
scaleW := float64(srcW) / float64(destW)
|
srcCopyPosition = src.Bounds()
|
||||||
scaleH := float64(srcH) / float64(destH)
|
destCopyPosition = image.Rect(offsetX, offsetY, destW+offsetX, destH+offsetY)
|
||||||
|
|
||||||
dest := image.NewRGBA(image.Rectangle{Max: image.Point{X: this.cfg.Width, Y: this.cfg.Height}})
|
case FitOutside:
|
||||||
|
|
||||||
|
var srcSmallestDim, offsetX, offsetY int
|
||||||
|
if srcW > srcH {
|
||||||
|
// Landscape
|
||||||
|
srcSmallestDim = srcH
|
||||||
|
offsetY = 0
|
||||||
|
offsetX = (srcW - srcH) / 2
|
||||||
|
} else {
|
||||||
|
// Portrait (or square)
|
||||||
|
srcSmallestDim = srcW
|
||||||
|
offsetY = (srcH - srcW) / 2
|
||||||
|
offsetX = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
srcCopyPosition = image.Rect(offsetX, offsetY, srcSmallestDim+offsetX, srcSmallestDim+offsetY)
|
||||||
|
destCopyPosition = image.Rect(0, 0, this.cfg.Width, this.cfg.Height)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidOption
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
dest := image.NewRGBA(image.Rect(0, 0, this.cfg.Width, this.cfg.Height))
|
||||||
|
|
||||||
|
// For a transparent destination, Op.Src is faster than Op.Over
|
||||||
|
|
||||||
switch this.cfg.Scale {
|
switch this.cfg.Scale {
|
||||||
|
|
||||||
case 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 NearestNeighbour:
|
case NearestNeighbour:
|
||||||
|
draw.NearestNeighbor.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
|
|
||||||
for y := 0; y < destH; y += 1 {
|
case BilinearFast:
|
||||||
for x := 0; x < destW; x += 1 {
|
draw.ApproxBiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
mapx := int(float64(x) * scaleW)
|
|
||||||
mapy := int(float64(y) * scaleH)
|
case BilinearAccurate:
|
||||||
dest.Set(x+offsetX, y+offsetY, src.At(mapx, mapy))
|
draw.BiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
}
|
|
||||||
|
case Bicubic:
|
||||||
|
draw.CatmullRom.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return dest, nil
|
||||||
|
|
||||||
return dest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *DirectThumbnailer) encode(dest image.Image) ([]byte, error) {
|
func (this *DirectThumbnailer) encode(dest image.Image) ([]byte, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user