mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-01 01:42:25 -05:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			109 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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)}
 | |
| }
 |