2015-07-26 16:44:51 +02:00
|
|
|
// Copyright 2014, 2015 by Sascha L. Teichmann
|
2014-09-10 18:58:12 +02:00
|
|
|
// Use of this source code is governed by the MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
2014-09-14 00:31:28 +02:00
|
|
|
package common
|
2014-09-10 18:58:12 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2015-07-22 12:06:21 +02:00
|
|
|
"bytes"
|
2014-09-21 17:30:19 +02:00
|
|
|
"errors"
|
2014-09-10 18:58:12 +02:00
|
|
|
"image"
|
2016-04-23 16:45:33 +02:00
|
|
|
"image/color"
|
2014-09-10 18:58:12 +02:00
|
|
|
"image/png"
|
2014-09-16 01:13:12 +02:00
|
|
|
"log"
|
2014-09-10 18:58:12 +02:00
|
|
|
"os"
|
2014-09-21 17:30:19 +02:00
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
"time"
|
2015-12-25 22:07:54 +01:00
|
|
|
|
2017-02-26 21:47:50 +01:00
|
|
|
"golang.org/x/crypto/blake2b"
|
|
|
|
|
2015-12-25 22:07:54 +01:00
|
|
|
"github.com/bamiaux/rez"
|
2014-09-10 18:58:12 +02:00
|
|
|
)
|
|
|
|
|
2015-12-25 22:07:54 +01:00
|
|
|
// ResizeFilter is used to scale down the pyramid tiles.
|
|
|
|
var ResizeFilter = rez.NewLanczosFilter(3)
|
|
|
|
|
2014-10-21 16:11:20 +02:00
|
|
|
var rrand uint32
|
|
|
|
var rrandmu sync.Mutex
|
2014-09-21 17:30:19 +02:00
|
|
|
|
|
|
|
func reseed() uint32 {
|
|
|
|
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func nextSuffix() string {
|
2014-10-21 16:11:20 +02:00
|
|
|
rrandmu.Lock()
|
|
|
|
r := rrand
|
2014-09-21 17:30:19 +02:00
|
|
|
if r == 0 {
|
|
|
|
r = reseed()
|
|
|
|
}
|
|
|
|
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
2014-10-21 16:11:20 +02:00
|
|
|
rrand = r
|
|
|
|
rrandmu.Unlock()
|
2014-09-21 17:30:19 +02:00
|
|
|
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
|
|
|
}
|
|
|
|
|
2015-07-22 12:06:21 +02:00
|
|
|
func EncodeToMem(img image.Image) ([]byte, error) {
|
|
|
|
var buf bytes.Buffer
|
2016-05-08 18:03:13 +02:00
|
|
|
enc := png.Encoder{CompressionLevel: png.BestCompression}
|
2015-07-22 12:06:21 +02:00
|
|
|
if err := enc.Encode(&buf, img); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2014-09-10 18:58:12 +02:00
|
|
|
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
|
|
|
|
}
|
2014-09-16 01:13:12 +02:00
|
|
|
|
2014-09-21 17:30:19 +02:00
|
|
|
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
|
2014-10-21 16:11:20 +02:00
|
|
|
rrand = reseed()
|
2014-09-21 17:30:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-04-23 16:45:33 +02:00
|
|
|
func LoadPNG(path string, bg color.RGBA) image.Image {
|
2014-09-16 01:13:12 +02:00
|
|
|
var err error
|
|
|
|
var file *os.File
|
|
|
|
if file, err = os.Open(path); err != nil {
|
2016-04-23 16:45:33 +02:00
|
|
|
return image.NewUniform(bg)
|
2014-09-16 01:13:12 +02:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
reader := bufio.NewReader(file)
|
|
|
|
var img image.Image
|
|
|
|
if img, err = png.Decode(reader); err != nil {
|
2015-08-24 10:58:46 +02:00
|
|
|
log.Printf("WARN: decoding '%s' failed: %s\n", path, err)
|
2016-04-23 16:45:33 +02:00
|
|
|
return image.NewUniform(bg)
|
2014-09-16 01:13:12 +02:00
|
|
|
}
|
|
|
|
return img
|
|
|
|
}
|
2016-05-08 12:33:17 +02:00
|
|
|
|
2017-02-26 21:47:50 +01:00
|
|
|
func HashImage(img *image.RGBA) []byte {
|
2016-05-08 17:33:51 +02:00
|
|
|
|
2017-02-26 21:47:50 +01:00
|
|
|
hash, _ := blake2b.New256(nil)
|
2016-05-08 12:33:17 +02:00
|
|
|
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)
|
|
|
|
}
|