// 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/png" "log" "os" "strconv" "sync" "time" ) 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, error) { var buf bytes.Buffer enc := png.Encoder{png.BestCompression} if err := enc.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil } 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) image.Image { var err error var file *os.File if file, err = os.Open(path); err != nil { return image.White } 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.White } return img }