// Copyright 2014, 2015 by Sascha L. Teichmann // Use of this source code is governed by the MIT license // that can be found in the LICENSE file. package common import ( "bufio" "bytes" "errors" "image" "image/color" "image/draw" "image/png" "log" "os" "strconv" "sync" "time" "golang.org/x/crypto/blake2b" "github.com/bamiaux/rez" ) // ResizeFilter is used to scale down the pyramid tiles. var ResizeFilter = rez.NewLanczosFilter(3) var rrand uint32 var rrandmu sync.Mutex func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { rrandmu.Lock() r := rrand if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes rrand = r rrandmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } func EncodeToMem(img image.Image) []byte { var buf bytes.Buffer enc := png.Encoder{CompressionLevel: png.BestCompression} if err := enc.Encode(&buf, img); err != nil { // This really should not happen. panic(err) } return buf.Bytes() } func SaveAsPNG(path string, img image.Image) (err error) { var file *os.File if file, err = os.Create(path); err != nil { return } writer := bufio.NewWriter(file) err = png.Encode(writer, img) writer.Flush() file.Close() return } func tmpName(tmpl string) (string, error) { tmpPre := tmpl + ".tmp" nconflict := 0 for i := 0; i < 10000; i++ { tmp := tmpPre + nextSuffix() if _, err := os.Stat(tmp); err != nil { if os.IsNotExist(err) { return tmp, nil } return "", err } if nconflict++; nconflict > 10 { nconflict = 0 rrand = reseed() } } return "", errors.New("cannot create temp name") } func SaveAsPNGAtomic(path string, img image.Image) (err error) { var tmpPath string if tmpPath, err = tmpName(path); err != nil { return } // Still a bit racy if err = SaveAsPNG(tmpPath, img); err != nil { return } return os.Rename(tmpPath, path) } func LoadPNG(path string, bg color.RGBA) image.Image { var err error var file *os.File if file, err = os.Open(path); err != nil { return image.NewUniform(bg) } defer file.Close() reader := bufio.NewReader(file) var img image.Image if img, err = png.Decode(reader); err != nil { log.Printf("WARN: decoding '%s' failed: %s\n", path, err) return image.NewUniform(bg) } return img } func HashImage(img *image.RGBA) []byte { hash, _ := blake2b.New256(nil) w, h := img.Rect.Dx()*4, img.Rect.Dy() pos := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y) for ; h > 0; h, pos = h-1, pos+img.Stride { hash.Write(img.Pix[pos : pos+w]) } return hash.Sum(nil) } func BackgroundImage(width, height int, bg color.RGBA) *image.RGBA { m := image.NewRGBA(image.Rect(0, 0, width, height)) draw.Draw(m, m.Bounds(), &image.Uniform{bg}, image.ZP, draw.Src) return m }