diff options
Diffstat (limited to 'vendor/github.com/buckket/go-blurhash/decode.go')
-rw-r--r-- | vendor/github.com/buckket/go-blurhash/decode.go | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/vendor/github.com/buckket/go-blurhash/decode.go b/vendor/github.com/buckket/go-blurhash/decode.go new file mode 100644 index 000000000..58cd7eb63 --- /dev/null +++ b/vendor/github.com/buckket/go-blurhash/decode.go @@ -0,0 +1,109 @@ +package blurhash + +import ( + "fmt" + "github.com/buckket/go-blurhash/base83" + "image" + "image/color" + "math" +) + +// An InvalidHashError occurs when the given hash is either too short or the length does not match its size flag. +type InvalidHashError string + +func (e InvalidHashError) Error() string { + return fmt.Sprintf("blurhash: %s", string(e)) +} + +// Components decodes and returns the number of x and y components in the given BlurHash. +func Components(hash string) (xComponents, yComponents int, err error) { + if len(hash) < 6 { + return 0, 0, InvalidHashError("hash is invalid (too short)") + } + + sizeFlag, err := base83.Decode(string(hash[0])) + if err != nil { + return 0, 0, err + } + + yComponents = (sizeFlag / 9) + 1 + xComponents = (sizeFlag % 9) + 1 + + if len(hash) != 4+2*xComponents*yComponents { + return 0, 0, InvalidHashError("hash is invalid (length mismatch)") + } + + return xComponents, yComponents, nil +} + +// Decode generates an image of the given BlurHash with a size of width and height. +// Punch is a multiplier that adjusts the contrast of the resulting image. +func Decode(hash string, width, height, punch int) (image.Image, error) { + xComp, yComp, err := Components(hash) + if err != nil { + return nil, err + } + + quantisedMaximumValue, err := base83.Decode(string(hash[1])) + if err != nil { + return nil, err + } + maximumValue := (float64(quantisedMaximumValue) + 1) / 166 + + if punch == 0 { + punch = 1 + } + + colors := make([][3]float64, xComp*yComp) + + for i := range colors { + if i == 0 { + value, err := base83.Decode(hash[2:6]) + if err != nil { + return nil, err + } + colors[i] = decodeDC(value) + } else { + value, err := base83.Decode(hash[4+i*2 : 6+i*2]) + if err != nil { + return nil, err + } + colors[i] = decodeAC(value, maximumValue*float64(punch)) + } + } + + img := image.NewNRGBA(image.Rect(0, 0, width, height)) + + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + var r, g, b float64 + for j := 0; j < yComp; j++ { + for i := 0; i < xComp; i++ { + basis := math.Cos(math.Pi*float64(x)*float64(i)/float64(width)) * + math.Cos(math.Pi*float64(y)*float64(j)/float64(height)) + pcolor := colors[i+j*xComp] + r += pcolor[0] * basis + g += pcolor[1] * basis + b += pcolor[2] * basis + } + } + img.SetNRGBA(x, y, color.NRGBA{R: uint8(linearTosRGB(r)), G: uint8(linearTosRGB(g)), B: uint8(linearTosRGB(b)), A: 255}) + } + } + + return img, nil +} + +func decodeDC(value int) [3]float64 { + return [3]float64{sRGBToLinear(value >> 16), sRGBToLinear(value >> 8 & 255), sRGBToLinear(value & 255)} +} + +func decodeAC(value int, maximumValue float64) [3]float64 { + quantR := math.Floor(float64(value) / (19 * 19)) + quantG := math.Mod(math.Floor(float64(value)/19), 19) + quantB := math.Mod(float64(value), 19) + sp := func(quant float64) float64 { + return signPow((quant-9)/9, 2.0) * maximumValue + } + return [3]float64{sp(quantR), sp(quantG), sp(quantB)} +} |