gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encode.go (4799B)


      1 package blurhash
      2 
      3 import (
      4 	"fmt"
      5 	"github.com/buckket/go-blurhash/base83"
      6 	"image"
      7 	"math"
      8 	"strings"
      9 )
     10 
     11 func init() {
     12 	initLinearTable(channelToLinear[:])
     13 }
     14 
     15 var channelToLinear [256]float64
     16 
     17 func initLinearTable(table []float64) {
     18 	for i := range table {
     19 		channelToLinear[i] = sRGBToLinear(i)
     20 	}
     21 }
     22 
     23 // An InvalidParameterError occurs when an invalid argument is passed to either the Decode or Encode function.
     24 type InvalidParameterError struct {
     25 	Value     int
     26 	Parameter string
     27 }
     28 
     29 func (e InvalidParameterError) Error() string {
     30 	return fmt.Sprintf("blurhash: %sComponents (%d) must be element of [1-9]", e.Parameter, e.Value)
     31 }
     32 
     33 // An EncodingError represents an error that occurred during the encoding of the given value.
     34 // This most likely means that your input image is invalid and can not be processed.
     35 type EncodingError string
     36 
     37 func (e EncodingError) Error() string {
     38 	return fmt.Sprintf("blurhash: %s", string(e))
     39 }
     40 
     41 // Encode calculates the Blurhash for an image using the given x and y component counts.
     42 // The x and y components have to be between 1 and 9 respectively.
     43 // The image must be of image.Image type.
     44 func Encode(xComponents int, yComponents int, rgba image.Image) (string, error) {
     45 	if xComponents < 1 || xComponents > 9 {
     46 		return "", InvalidParameterError{xComponents, "x"}
     47 	}
     48 	if yComponents < 1 || yComponents > 9 {
     49 		return "", InvalidParameterError{yComponents, "y"}
     50 	}
     51 
     52 	var blurhash strings.Builder
     53 	blurhash.Grow(4 + 2*xComponents*yComponents)
     54 
     55 	// Size Flag
     56 	str, err := base83.Encode((xComponents-1)+(yComponents-1)*9, 1)
     57 	if err != nil {
     58 		return "", EncodingError("could not encode size flag")
     59 	}
     60 	blurhash.WriteString(str)
     61 
     62 	factors := make([]float64, yComponents*xComponents*3)
     63 	multiplyBasisFunction(rgba, factors, xComponents, yComponents)
     64 
     65 	var maximumValue float64
     66 	var quantisedMaximumValue int
     67 	var acCount = xComponents*yComponents - 1
     68 	if acCount > 0 {
     69 		var actualMaximumValue float64
     70 		for i := 0; i < acCount*3; i++ {
     71 			actualMaximumValue = math.Max(math.Abs(factors[i+3]), actualMaximumValue)
     72 		}
     73 		quantisedMaximumValue = int(math.Max(0, math.Min(82, math.Floor(actualMaximumValue*166-0.5))))
     74 		maximumValue = (float64(quantisedMaximumValue) + 1) / 166
     75 	} else {
     76 		maximumValue = 1
     77 	}
     78 
     79 	// Quantised max AC component
     80 	str, err = base83.Encode(quantisedMaximumValue, 1)
     81 	if err != nil {
     82 		return "", EncodingError("could not encode quantised max AC component")
     83 	}
     84 	blurhash.WriteString(str)
     85 
     86 	// DC value
     87 	str, err = base83.Encode(encodeDC(factors[0], factors[1], factors[2]), 4)
     88 	if err != nil {
     89 		return "", EncodingError("could not encode DC value")
     90 	}
     91 	blurhash.WriteString(str)
     92 
     93 	// AC values
     94 	for i := 0; i < acCount; i++ {
     95 		str, err = base83.Encode(encodeAC(factors[3+(i*3+0)], factors[3+(i*3+1)], factors[3+(i*3+2)], maximumValue), 2)
     96 		if err != nil {
     97 			return "", EncodingError("could not encode AC value")
     98 		}
     99 		blurhash.WriteString(str)
    100 	}
    101 
    102 	if blurhash.Len() != 4+2*xComponents*yComponents {
    103 		return "", EncodingError("hash does not match expected size")
    104 	}
    105 
    106 	return blurhash.String(), nil
    107 }
    108 
    109 func multiplyBasisFunction(rgba image.Image, factors []float64, xComponents int, yComponents int) {
    110 	height := rgba.Bounds().Max.Y
    111 	width := rgba.Bounds().Max.X
    112 
    113 	xvalues := make([][]float64, xComponents)
    114 	for xComponent := 0; xComponent < xComponents; xComponent++ {
    115 		xvalues[xComponent] = make([]float64, width)
    116 		for x := 0; x < width; x++ {
    117 			xvalues[xComponent][x] = math.Cos(math.Pi * float64(xComponent) * float64(x) / float64(width))
    118 		}
    119 	}
    120 
    121 	yvalues := make([][]float64, yComponents)
    122 	for yComponent := 0; yComponent < yComponents; yComponent++ {
    123 		yvalues[yComponent] = make([]float64, height)
    124 		for y := 0; y < height; y++ {
    125 			yvalues[yComponent][y] = math.Cos(math.Pi * float64(yComponent) * float64(y) / float64(height))
    126 		}
    127 	}
    128 
    129 	for y := 0; y < height; y++ {
    130 		for x := 0; x < width; x++ {
    131 			rt, gt, bt, _ := rgba.At(x, y).RGBA()
    132 			lr := channelToLinear[rt>>8]
    133 			lg := channelToLinear[gt>>8]
    134 			lb := channelToLinear[bt>>8]
    135 
    136 			for yc := 0; yc < yComponents; yc++ {
    137 				for xc := 0; xc < xComponents; xc++ {
    138 
    139 					scale := 1 / float64(width*height)
    140 
    141 					if xc != 0 || yc != 0 {
    142 						scale = 2 / float64(width*height)
    143 					}
    144 
    145 					basis := xvalues[xc][x] * yvalues[yc][y]
    146 					factors[0+xc*3+yc*3*xComponents] += lr * basis * scale
    147 					factors[1+xc*3+yc*3*xComponents] += lg * basis * scale
    148 					factors[2+xc*3+yc*3*xComponents] += lb * basis * scale
    149 				}
    150 			}
    151 		}
    152 	}
    153 }
    154 
    155 func encodeDC(r, g, b float64) int {
    156 	return (linearTosRGB(r) << 16) + (linearTosRGB(g) << 8) + linearTosRGB(b)
    157 }
    158 
    159 func encodeAC(r, g, b, maximumValue float64) int {
    160 	quant := func(f float64) int {
    161 		return int(math.Max(0, math.Min(18, math.Floor(signPow(f/maximumValue, 0.5)*9+9.5))))
    162 	}
    163 	return quant(r)*19*19 + quant(g)*19 + quant(b)
    164 }