summaryrefslogtreecommitdiff
path: root/vendor/github.com/buckket/go-blurhash/decode.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/buckket/go-blurhash/decode.go')
-rw-r--r--vendor/github.com/buckket/go-blurhash/decode.go109
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)}
+}