Compare commits
4 Commits
187d71cb5b
...
f1d98b605f
Author | SHA1 | Date | |
---|---|---|---|
f1d98b605f | |||
d4764ce485 | |||
4fcf12dff2 | |||
6564d9c4fa |
@ -14,7 +14,7 @@ steps:
|
|||||||
- apk --no-cache add git go-bindata
|
- apk --no-cache add git go-bindata
|
||||||
- go generate -v
|
- go generate -v
|
||||||
- go get -v -d
|
- go get -v -d
|
||||||
- go build -v -o youp0m
|
- go build -v -ldflags="-s -w" -o youp0m
|
||||||
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
||||||
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ steps:
|
|||||||
- apk --no-cache add git go-bindata
|
- apk --no-cache add git go-bindata
|
||||||
- go generate -v
|
- go generate -v
|
||||||
- go get -v -d
|
- go get -v -d
|
||||||
- go build -v -o youp0m
|
- go build -v -ldflags="-s -w" -o youp0m
|
||||||
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
||||||
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ steps:
|
|||||||
- apk --no-cache add git go-bindata
|
- apk --no-cache add git go-bindata
|
||||||
- go generate -v
|
- go generate -v
|
||||||
- go get -v -d
|
- go get -v -d
|
||||||
- go build -v -o youp0m
|
- go build -v -ldflags="-s -w" -o youp0m
|
||||||
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
- wget -O Dockerfile https://ankh.serekh.nemunai.re/local/Dockerfile-youp0m
|
||||||
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
- wget -O entrypoint.sh https://ankh.serekh.nemunai.re/local/entrypoint.sh-youp0m && chmod +x entrypoint.sh
|
||||||
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
bindata.go
|
|
||||||
youp0m
|
youp0m
|
||||||
|
vendor
|
37
api.go
37
api.go
@ -12,24 +12,31 @@ import (
|
|||||||
|
|
||||||
type DispatchFunction func(*User, []string, io.ReadCloser) (interface{}, error)
|
type DispatchFunction func(*User, []string, io.ReadCloser) (interface{}, error)
|
||||||
|
|
||||||
var apiRoutes = map[string]*(map[string]struct {
|
type apiHandler struct {
|
||||||
|
PE *PictureExplorer
|
||||||
|
Authenticate func(*http.Request) *User
|
||||||
|
routes map[string](map[string]struct {
|
||||||
|
AuthFunction
|
||||||
|
DispatchFunction
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIHandler(pe *PictureExplorer, authenticate func(*http.Request) *User) *apiHandler {
|
||||||
|
return &apiHandler{
|
||||||
|
pe,
|
||||||
|
authenticate,
|
||||||
|
map[string](map[string]struct {
|
||||||
AuthFunction
|
AuthFunction
|
||||||
DispatchFunction
|
DispatchFunction
|
||||||
}){
|
}){
|
||||||
"images": &ApiImagesRouting,
|
"images": ApiImagesRouting(pe),
|
||||||
"next": &ApiNextImagesRouting,
|
"next": ApiNextImagesRouting(pe),
|
||||||
"version": &ApiVersionRouting,
|
"version": ApiVersionRouting,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiHandler struct {
|
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
Authenticate func(*http.Request) *User
|
|
||||||
}
|
|
||||||
|
|
||||||
func ApiHandler(Authenticate func(*http.Request) *User) apiHandler {
|
|
||||||
return apiHandler{Authenticate}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user := a.Authenticate(r)
|
user := a.Authenticate(r)
|
||||||
|
|
||||||
log.Printf("Handling %s API request from %s: %s %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, user, r.UserAgent())
|
log.Printf("Handling %s API request from %s: %s %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, user, r.UserAgent())
|
||||||
@ -55,8 +62,8 @@ func (a apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Route request
|
// Route request
|
||||||
if len(sURL) == 0 {
|
if len(sURL) == 0 {
|
||||||
err = errors.New(fmt.Sprintf("No action provided"))
|
err = errors.New(fmt.Sprintf("No action provided"))
|
||||||
} else if h, ok := apiRoutes[sURL[0]]; ok {
|
} else if h, ok := a.routes[sURL[0]]; ok {
|
||||||
if f, ok := (*h)[r.Method]; ok {
|
if f, ok := h[r.Method]; ok {
|
||||||
if f.AuthFunction(user, sURL[1:]) {
|
if f.AuthFunction(user, sURL[1:]) {
|
||||||
ret, err = f.DispatchFunction(user, sURL[1:], r.Body)
|
ret, err = f.DispatchFunction(user, sURL[1:], r.Body)
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,78 +5,88 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ApiImagesRouting = map[string]struct {
|
func ApiImagesRouting(pe *PictureExplorer) map[string]struct {
|
||||||
AuthFunction
|
AuthFunction
|
||||||
DispatchFunction
|
DispatchFunction
|
||||||
} {
|
} {
|
||||||
"GET": {PublicPage, listImages},
|
return map[string]struct {
|
||||||
"POST": {PublicPage, addImage},
|
|
||||||
"DELETE": {PrivatePage, hideImage},
|
|
||||||
}
|
|
||||||
|
|
||||||
var ApiNextImagesRouting = map[string]struct {
|
|
||||||
AuthFunction
|
AuthFunction
|
||||||
DispatchFunction
|
DispatchFunction
|
||||||
}{
|
}{
|
||||||
"GET": {PrivatePage, listNextImages},
|
"GET": {PublicPage, pe.listImages},
|
||||||
"POST": {PublicPage, addImage},
|
"POST": {PublicPage, pe.addImage},
|
||||||
"DELETE": {PrivatePage, deleteImage},
|
"DELETE": {PrivatePage, pe.hideImage},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listImages(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func ApiNextImagesRouting(pe *PictureExplorer) map[string]struct {
|
||||||
|
AuthFunction
|
||||||
|
DispatchFunction
|
||||||
|
} {
|
||||||
|
return map[string]struct {
|
||||||
|
AuthFunction
|
||||||
|
DispatchFunction
|
||||||
|
}{
|
||||||
|
"GET": {PrivatePage, pe.listNextImages},
|
||||||
|
"POST": {PublicPage, pe.addImage},
|
||||||
|
"DELETE": {PrivatePage, pe.deleteImage},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *PictureExplorer) listImages(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return GetPublishedImages()
|
return pe.GetPublishedImages()
|
||||||
} else if args[0] == "last" {
|
} else if args[0] == "last" {
|
||||||
return GetLastImage()
|
return pe.GetLastImage()
|
||||||
} else {
|
} else {
|
||||||
return GetPublishedImage(args[0])
|
return pe.GetPublishedImage(args[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listNextImages(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func (pe *PictureExplorer) listNextImages(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return GetNextImages()
|
return pe.GetNextImages()
|
||||||
} else if len(args) >= 2 && args[1] == "publish" {
|
} else if len(args) >= 2 && args[1] == "publish" {
|
||||||
if pict, err := GetNextImage(args[0]); err != nil {
|
if pict, err := pe.GetNextImage(args[0]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if err := pict.Publish(); err != nil {
|
} else if err := pe.Publish(pict); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return GetNextImage(args[0])
|
return pe.GetNextImage(args[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func (pe *PictureExplorer) addImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, errors.New("Need an image identifier to create")
|
return nil, errors.New("Need an image identifier to create")
|
||||||
} else if err := AddImage(args[0], body); err != nil {
|
} else if err := pe.AddImage(args[0], body); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hideImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func (pe *PictureExplorer) hideImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, errors.New("Need an image identifier to delete")
|
return nil, errors.New("Need an image identifier to delete")
|
||||||
} else if pict, err := GetPublishedImage(args[0]); err != nil {
|
} else if pict, err := pe.GetPublishedImage(args[0]); err != nil {
|
||||||
return nil, errors.New("No matching image")
|
return nil, errors.New("No matching image")
|
||||||
} else if err := pict.Unpublish(); err != nil {
|
} else if err := pe.Unpublish(pict); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func (pe *PictureExplorer) deleteImage(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, errors.New("Need an image identifier to delete")
|
return nil, errors.New("Need an image identifier to delete")
|
||||||
} else if pict, err := GetNextImage(args[0]); err != nil {
|
} else if pict, err := pe.GetNextImage(args[0]); err != nil {
|
||||||
return nil, errors.New("No matching image")
|
return nil, errors.New("No matching image")
|
||||||
} else if err := pict.Remove(); err != nil {
|
} else if err := pe.Remove(pict); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
32
assets-dev.go
Normal file
32
assets-dev.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//go:build dev
|
||||||
|
// +build dev
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Assets http.FileSystem
|
||||||
|
StaticDir string = "static/"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&StaticDir, "static", StaticDir, "Directory containing static files")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeStaticOptions() error {
|
||||||
|
StaticDir, _ = filepath.Abs(StaticDir)
|
||||||
|
if _, err := os.Stat(StaticDir); os.IsNotExist(err) {
|
||||||
|
StaticDir, _ = filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), "static"))
|
||||||
|
if _, err := os.Stat(StaticDir); os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assets = http.Dir(StaticDir)
|
||||||
|
return nil
|
||||||
|
}
|
28
assets.go
Normal file
28
assets.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//go:build !dev
|
||||||
|
// +build !dev
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed static/* static/js/* static/css/*
|
||||||
|
var _assets embed.FS
|
||||||
|
|
||||||
|
var Assets http.FileSystem
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sub, err := fs.Sub(_assets, "static")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Unable to cd to static/ directory:", err)
|
||||||
|
}
|
||||||
|
Assets = http.FS(sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeStaticOptions() error {
|
||||||
|
return nil
|
||||||
|
}
|
44
backend.go
Normal file
44
backend.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var existing_backends []string
|
||||||
|
|
||||||
|
type BackendSelector string
|
||||||
|
|
||||||
|
func (s *BackendSelector) String() string {
|
||||||
|
return string(*s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BackendSelector) Set(v string) error {
|
||||||
|
found := false
|
||||||
|
for _, b := range existing_backends {
|
||||||
|
if strings.ToLower(v) == b {
|
||||||
|
found = true
|
||||||
|
tmp := BackendSelector(v)
|
||||||
|
s = &tmp
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("%q is not a known file backend (existing backends: %v)", v, existing_backends)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileBackend interface {
|
||||||
|
DeletePicture(string, string) error
|
||||||
|
ServeFile() http.Handler
|
||||||
|
GetPicture(string, string, io.Writer) error
|
||||||
|
GetPictureInfo(string, string) (*Picture, error)
|
||||||
|
ListPictures(string) ([]*Picture, error)
|
||||||
|
MovePicture(string, string, string) error
|
||||||
|
PutPicture(string, string, *image.Image) error
|
||||||
|
}
|
106
backend_local.go
Normal file
106
backend_local.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
existing_backends = append(existing_backends, "local")
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalFileBackend struct {
|
||||||
|
BaseDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) getPath(box, name string) string {
|
||||||
|
return path.Join(l.BaseDir, box, name+".jpg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) DeletePicture(box, name string) error {
|
||||||
|
return os.Remove(l.getPath(box, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) ServeFile() http.Handler {
|
||||||
|
return http.FileServer(http.Dir(l.BaseDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) createPictureInfo(local_path, name string, file os.FileInfo) *Picture {
|
||||||
|
return &Picture{
|
||||||
|
local_path, // Path
|
||||||
|
name, // Basename
|
||||||
|
name[:len(name)-len(path.Ext(name))], // Sanitized filename
|
||||||
|
file.ModTime(), // UploadTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) GetPicture(box, name string, w io.Writer) error {
|
||||||
|
local_path := l.getPath(box, name)
|
||||||
|
|
||||||
|
fd, err := os.Open(local_path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(w, fd)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) GetPictureInfo(box, name string) (*Picture, error) {
|
||||||
|
local_path := l.getPath(box, name)
|
||||||
|
|
||||||
|
file, err := os.Stat(local_path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.createPictureInfo(path.Join(box, name+".jpg"), name, file), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) ListPictures(box string) ([]*Picture, error) {
|
||||||
|
files, err := ioutil.ReadDir(path.Join(l.BaseDir, box))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pictures := make([]*Picture, 0)
|
||||||
|
for _, file := range files {
|
||||||
|
if !file.IsDir() {
|
||||||
|
pictures = append(pictures, l.createPictureInfo(path.Join(box, file.Name()), file.Name(), file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pictures, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) MovePicture(box_from, box_to, name string) error {
|
||||||
|
newpath := l.getPath(box_to, name)
|
||||||
|
|
||||||
|
err := os.Rename(
|
||||||
|
l.getPath(box_from, name),
|
||||||
|
newpath,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Chtimes(newpath, time.Now(), time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LocalFileBackend) PutPicture(box, name string, img *image.Image) error {
|
||||||
|
fd, err := os.OpenFile(l.getPath(box, name), os.O_RDWR|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
return jpeg.Encode(fd, *img, nil)
|
||||||
|
}
|
239
backend_s3.go
Normal file
239
backend_s3.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
existing_backends = append(existing_backends, "s3")
|
||||||
|
}
|
||||||
|
|
||||||
|
type S3FileBackend struct {
|
||||||
|
Endpoint string
|
||||||
|
Region string
|
||||||
|
Bucket string
|
||||||
|
AccessKey string
|
||||||
|
SecretKey string
|
||||||
|
PathStyle bool
|
||||||
|
BaseDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) getPath(box, name string) string {
|
||||||
|
if l.BaseDir == "" {
|
||||||
|
return path.Join(box, name+".jpg")
|
||||||
|
} else {
|
||||||
|
return path.Join(l.BaseDir, box, name+".jpg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) newSession() (*session.Session, error) {
|
||||||
|
return session.NewSession(&aws.Config{
|
||||||
|
Credentials: credentials.NewStaticCredentials(l.AccessKey, l.SecretKey, ""),
|
||||||
|
Endpoint: &l.Endpoint,
|
||||||
|
Region: &l.Region,
|
||||||
|
S3ForcePathStyle: &l.PathStyle,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) DeletePicture(box, name string) error {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(l.getPath(box, name))
|
||||||
|
|
||||||
|
input := &s3.DeleteObjectsInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
Delete: &s3.Delete{
|
||||||
|
Objects: []*s3.ObjectIdentifier{
|
||||||
|
{
|
||||||
|
Key: aws.String(l.getPath(box, name)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s3.New(s).DeleteObjects(input)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) ServeFile() http.Handler {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s3.New(s).GetObject(&s3.GetObjectInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
Key: aws.String(r.URL.Path),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(w, result.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) createPictureInfo(local_path, name string, file *s3.Object) *Picture {
|
||||||
|
return &Picture{
|
||||||
|
local_path, // Path
|
||||||
|
name, // Basename
|
||||||
|
name[:len(name)-len(path.Ext(name))], // Sanitized filename
|
||||||
|
*file.LastModified, // UploadTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) GetPicture(box, name string, w io.Writer) error {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s3_path := l.getPath(box, name)
|
||||||
|
|
||||||
|
input := &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
Key: aws.String(s3_path),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s3.New(s).GetObject(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(w, result.Body)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) GetPictureInfo(box, name string) (*Picture, error) {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
input := &s3.ListObjectsInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
Prefix: aws.String(l.getPath(box, name)),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s3.New(s).ListObjects(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pictures []*Picture
|
||||||
|
for _, file := range result.Contents {
|
||||||
|
pictures = append(pictures, l.createPictureInfo(*file.Key, path.Base(*file.Key), file))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pictures) == 0 {
|
||||||
|
return nil, fmt.Errorf("Object not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pictures[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) ListPictures(box string) ([]*Picture, error) {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
input := &s3.ListObjectsInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.BaseDir == "" {
|
||||||
|
input.Prefix = aws.String(box)
|
||||||
|
} else {
|
||||||
|
input.Prefix = aws.String(path.Join(l.BaseDir, box))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s3.New(s).ListObjects(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pictures := make([]*Picture, 0)
|
||||||
|
for _, file := range result.Contents {
|
||||||
|
pictures = append(pictures, l.createPictureInfo(path.Join(box, *file.Key), path.Base(*file.Key), file))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pictures, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) MovePicture(box_from, box_to, name string) error {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(l.getPath(box_from, name), l.getPath(box_to, name))
|
||||||
|
|
||||||
|
_, err = s3.New(s).CopyObject(&s3.CopyObjectInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
CopySource: aws.String(path.Join("", l.Bucket, l.getPath(box_from, name))),
|
||||||
|
Key: aws.String(l.getPath(box_to, name)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(l.getPath(box_from, name))
|
||||||
|
|
||||||
|
input := &s3.DeleteObjectsInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
Delete: &s3.Delete{
|
||||||
|
Objects: []*s3.ObjectIdentifier{
|
||||||
|
{
|
||||||
|
Key: aws.String(l.getPath(box_from, name)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s3.New(s).DeleteObjects(input)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *S3FileBackend) PutPicture(box, name string, img *image.Image) error {
|
||||||
|
s, err := l.newSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bbuf := new(bytes.Buffer)
|
||||||
|
err = jpeg.Encode(bbuf, *img, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s3manager.NewUploader(s).Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String(l.Bucket),
|
||||||
|
ACL: aws.String("public-read"),
|
||||||
|
Key: aws.String(l.getPath(box, name)),
|
||||||
|
Body: bbuf,
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
3
go.mod
3
go.mod
@ -1,8 +1,9 @@
|
|||||||
module git.nemunai.re/youp0m
|
module git.nemunai.re/youp0m
|
||||||
|
|
||||||
go 1.10
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/aws/aws-sdk-go v1.44.91
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b
|
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b
|
||||||
)
|
)
|
||||||
|
17
go.sum
17
go.sum
@ -1,4 +1,21 @@
|
|||||||
|
github.com/aws/aws-sdk-go v1.44.91 h1:SRWmuX7PTyhBdLuvSfM7KWrWISJsrRsUPcFDSFduRxY=
|
||||||
|
github.com/aws/aws-sdk-go v1.44.91/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs=
|
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs=
|
||||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
|
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
|
||||||
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@ -10,10 +10,10 @@ type imagesHandler struct {
|
|||||||
name string
|
name string
|
||||||
auth func(*http.Request) bool
|
auth func(*http.Request) bool
|
||||||
hndlr http.Handler
|
hndlr http.Handler
|
||||||
getter func(fname string) (Picture, error)
|
getter func(fname string) (*Picture, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImagesHandler(name string, auth func(*http.Request) bool, hndlr http.Handler, getter func(fname string) (Picture, error)) imagesHandler {
|
func ImagesHandler(name string, auth func(*http.Request) bool, hndlr http.Handler, getter func(fname string) (*Picture, error)) imagesHandler {
|
||||||
return imagesHandler{name, auth, hndlr, getter}
|
return imagesHandler{name, auth, hndlr, getter}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ func (i imagesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
r.URL.Path = "/" + pict.basename
|
r.URL.Path = "/" + pict.path
|
||||||
i.hndlr.ServeHTTP(w, r)
|
i.hndlr.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
46
main.go
46
main.go
@ -9,15 +9,13 @@ import (
|
|||||||
|
|
||||||
var mux = http.NewServeMux()
|
var mux = http.NewServeMux()
|
||||||
|
|
||||||
var PublishedImgDir string
|
|
||||||
var NextImgDir string
|
|
||||||
var ThumbsDir string
|
var ThumbsDir string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
bind := flag.String("bind", ":8080", "Bind port/socket")
|
bind := flag.String("bind", ":8080", "Bind port/socket")
|
||||||
htpasswd_file := flag.String("htpasswd", "", "Admin passwords file, Apache htpasswd format")
|
htpasswd_file := flag.String("htpasswd", "", "Admin passwords file, Apache htpasswd format")
|
||||||
flag.StringVar(&PublishedImgDir, "publishedimgdir", "./images/published/", "Directory where save published pictures")
|
publishedImgDir := flag.String("publishedimgdir", "published/", "Directory where save published pictures")
|
||||||
flag.StringVar(&NextImgDir, "nextimgdir", "./images/next/", "Directory where save pictures to review")
|
nextImgDir := flag.String("nextimgdir", "next/", "Directory where save pictures to review")
|
||||||
flag.StringVar(&ThumbsDir, "thumbsdir", "./images/thumbs/", "Directory where generate thumbs")
|
flag.StringVar(&ThumbsDir, "thumbsdir", "./images/thumbs/", "Directory where generate thumbs")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -29,22 +27,12 @@ func main() {
|
|||||||
if htpasswd, err = NewHtpasswd(*htpasswd_file); htpasswd == nil {
|
if htpasswd, err = NewHtpasswd(*htpasswd_file); htpasswd == nil {
|
||||||
log.Fatal("Unable to parse htpasswd:", err)
|
log.Fatal("Unable to parse htpasswd:", err)
|
||||||
}
|
}
|
||||||
} else if NextImgDir == "./images/next/" {
|
} else if *nextImgDir == "./images/next/" {
|
||||||
log.Println("Disable admin interface, images will be published without moderation")
|
log.Println("Disable admin interface, images will be published without moderation")
|
||||||
NextImgDir = PublishedImgDir
|
nextImgDir = publishedImgDir
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Checking paths...")
|
log.Println("Checking paths...")
|
||||||
if _, err := os.Stat(PublishedImgDir); os.IsNotExist(err) {
|
|
||||||
if err := os.MkdirAll(PublishedImgDir, 0755); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(NextImgDir); os.IsNotExist(err) {
|
|
||||||
if err := os.MkdirAll(NextImgDir, 0755); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(ThumbsDir); os.IsNotExist(err) {
|
if _, err := os.Stat(ThumbsDir); os.IsNotExist(err) {
|
||||||
if err := os.MkdirAll(ThumbsDir, 0755); err != nil {
|
if err := os.MkdirAll(ThumbsDir, 0755); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -59,35 +47,42 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
serveStaticAsset(w, r, "index.html")
|
r.URL.Path = "/"
|
||||||
|
http.FileServer(Assets).ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.Handle("/api/", http.StripPrefix("/api", ApiHandler(authFunc)))
|
pe := &PictureExplorer{
|
||||||
|
FileBackend: &LocalFileBackend{"./images/"},
|
||||||
|
PublishedImgDir: *publishedImgDir,
|
||||||
|
NextImgDir: *nextImgDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.Handle("/api/", http.StripPrefix("/api", NewAPIHandler(pe, authFunc)))
|
||||||
|
|
||||||
mux.Handle("/images/", http.StripPrefix("/images", ImagesHandler(
|
mux.Handle("/images/", http.StripPrefix("/images", ImagesHandler(
|
||||||
"images",
|
"images",
|
||||||
func(*http.Request) bool { return true },
|
func(*http.Request) bool { return true },
|
||||||
http.FileServer(http.Dir(PublishedImgDir)),
|
pe.ServeFile(),
|
||||||
GetPublishedImage,
|
pe.GetPublishedImage,
|
||||||
)))
|
)))
|
||||||
mux.Handle("/images/next/", http.StripPrefix("/images/next", ImagesHandler(
|
mux.Handle("/images/next/", http.StripPrefix("/images/next", ImagesHandler(
|
||||||
"next",
|
"next",
|
||||||
func(r *http.Request) bool { return authFunc(r) != nil },
|
func(r *http.Request) bool { return authFunc(r) != nil },
|
||||||
http.FileServer(http.Dir(NextImgDir)),
|
pe.ServeFile(),
|
||||||
GetNextImage,
|
pe.GetNextImage,
|
||||||
)))
|
)))
|
||||||
|
|
||||||
mux.Handle("/images/thumbs/", http.StripPrefix("/images/thumbs", ImagesHandler(
|
mux.Handle("/images/thumbs/", http.StripPrefix("/images/thumbs", ImagesHandler(
|
||||||
"thumbs",
|
"thumbs",
|
||||||
func(*http.Request) bool { return true },
|
func(*http.Request) bool { return true },
|
||||||
http.FileServer(http.Dir(ThumbsDir)),
|
http.FileServer(http.Dir(ThumbsDir)),
|
||||||
GetPublishedImage,
|
pe.GetPublishedImage,
|
||||||
)))
|
)))
|
||||||
mux.Handle("/images/next/thumbs/", http.StripPrefix("/images/next/thumbs", ImagesHandler(
|
mux.Handle("/images/next/thumbs/", http.StripPrefix("/images/next/thumbs", ImagesHandler(
|
||||||
"nexthumbs",
|
"nexthumbs",
|
||||||
func(r *http.Request) bool { return authFunc(r) != nil },
|
func(r *http.Request) bool { return authFunc(r) != nil },
|
||||||
http.FileServer(http.Dir(ThumbsDir)),
|
http.FileServer(http.Dir(ThumbsDir)),
|
||||||
GetNextImage,
|
pe.GetNextImage,
|
||||||
)))
|
)))
|
||||||
|
|
||||||
mux.HandleFunc("/admin/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/admin/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -95,7 +90,8 @@ func main() {
|
|||||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"YouP0m\"")
|
w.Header().Set("WWW-Authenticate", "Basic realm=\"YouP0m\"")
|
||||||
http.Error(w, "You are not allowed to perform this request.", http.StatusUnauthorized)
|
http.Error(w, "You are not allowed to perform this request.", http.StatusUnauthorized)
|
||||||
} else {
|
} else {
|
||||||
serveStaticAsset(w, r, "admin.html")
|
r.URL.Path = "/admin.html"
|
||||||
|
http.FileServer(Assets).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
149
picture.go
149
picture.go
@ -8,16 +8,20 @@ import (
|
|||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PictureExplorer struct {
|
||||||
|
FileBackend
|
||||||
|
PublishedImgDir string
|
||||||
|
NextImgDir string
|
||||||
|
}
|
||||||
|
|
||||||
type Picture struct {
|
type Picture struct {
|
||||||
path string
|
path string
|
||||||
basename string
|
basename string
|
||||||
@ -25,7 +29,7 @@ type Picture struct {
|
|||||||
UploadTime time.Time `json:"upload_time"`
|
UploadTime time.Time `json:"upload_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ByUploadTime []Picture
|
type ByUploadTime []*Picture
|
||||||
|
|
||||||
func (a ByUploadTime) Len() int { return len(a) }
|
func (a ByUploadTime) Len() int { return len(a) }
|
||||||
func (a ByUploadTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
func (a ByUploadTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
@ -33,137 +37,84 @@ func (a ByUploadTime) Less(i, j int) bool {
|
|||||||
return a[i].UploadTime.Sub(a[j].UploadTime).Nanoseconds() < 0
|
return a[i].UploadTime.Sub(a[j].UploadTime).Nanoseconds() < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImages(dir string) ([]Picture, error) {
|
func (e *PictureExplorer) GetNextImages() (pictures []*Picture, err error) {
|
||||||
if files, err := ioutil.ReadDir(dir); err != nil {
|
pictures, err = e.ListPictures(e.NextImgDir)
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
pictures := make([]Picture, 0)
|
|
||||||
for _, file := range files {
|
|
||||||
if !file.IsDir() {
|
|
||||||
filename := file.Name()
|
|
||||||
pictures = append(pictures, Picture{
|
|
||||||
filepath.Join(dir, filename), // Path
|
|
||||||
filename, // Basename
|
|
||||||
filename[:len(filename)-len(filepath.Ext(filename))], // Sanitized filename
|
|
||||||
file.ModTime(), // UploadTime
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(ByUploadTime(pictures))
|
sort.Sort(ByUploadTime(pictures))
|
||||||
return pictures, nil
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNextImages() ([]Picture, error) {
|
func (e *PictureExplorer) GetPublishedImages() (pictures []*Picture, err error) {
|
||||||
return getImages(NextImgDir)
|
pictures, err = e.ListPictures(e.PublishedImgDir)
|
||||||
|
sort.Sort(ByUploadTime(pictures))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPublishedImages() ([]Picture, error) {
|
func (e *PictureExplorer) GetLastImage() (*Picture, error) {
|
||||||
return getImages(PublishedImgDir)
|
picts, err := e.GetPublishedImages()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLastImage() (Picture, error) {
|
|
||||||
if picts, err := GetPublishedImages(); err != nil {
|
|
||||||
return Picture{}, err
|
|
||||||
} else {
|
|
||||||
return picts[len(picts)-1], nil
|
return picts[len(picts)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *PictureExplorer) GetPublishedImage(fname string) (*Picture, error) {
|
||||||
|
return e.GetPictureInfo(e.PublishedImgDir, fname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImage(flist func() ([]Picture, error), fname string) (Picture, error) {
|
func (e *PictureExplorer) GetNextImage(fname string) (*Picture, error) {
|
||||||
if picts, err := flist(); err != nil {
|
return e.GetPictureInfo(e.NextImgDir, fname)
|
||||||
return Picture{}, err
|
|
||||||
} else if pid, err := strconv.Atoi(fname); err == nil {
|
|
||||||
if pid < len(picts) {
|
|
||||||
return picts[pid], nil
|
|
||||||
} else {
|
|
||||||
return Picture{}, errors.New("Invalid picture identifier")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, pict := range picts {
|
|
||||||
if pict.Name == fname || pict.basename == fname {
|
|
||||||
return pict, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Picture{}, errors.New("No such picture")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPublishedImage(fname string) (Picture, error) {
|
func (e *PictureExplorer) IsUniqueName(filename string) bool {
|
||||||
return getImage(GetPublishedImages, fname)
|
if pict, _ := e.GetPublishedImage(filename); pict != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func GetNextImage(fname string) (Picture, error) {
|
|
||||||
return getImage(GetNextImages, fname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UniqueImage(filename string) bool {
|
|
||||||
if pict, _ := GetPublishedImage(filename); pict.path != "" {
|
|
||||||
return false
|
return false
|
||||||
} else {
|
}
|
||||||
if pict, _ := GetNextImage(filename); pict.path != "" {
|
|
||||||
|
if pict, _ := e.GetNextImage(filename); pict != nil {
|
||||||
return false
|
return false
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddImage(filename string, blob io.ReadCloser) error {
|
func (e *PictureExplorer) AddImage(filename string, blob io.ReadCloser) error {
|
||||||
// Check the name is not already used
|
// Check the name is not already used
|
||||||
if ok := UniqueImage(filename); !ok {
|
if ok := e.IsUniqueName(filename); !ok {
|
||||||
return errors.New("This filename is already used, please choose another one.")
|
return errors.New("This filename is already used, please choose another one.")
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to JPEG
|
// Convert to JPEG
|
||||||
} else if img, _, err := image.Decode(base64.NewDecoder(base64.StdEncoding, blob)); err != nil {
|
img, _, err := image.Decode(base64.NewDecoder(base64.StdEncoding, blob))
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
} else if fw, err := os.Create(filepath.Join(NextImgDir, filename+".jpg")); err != nil {
|
if err := e.PutPicture(e.NextImgDir, filename, &img); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := jpeg.Encode(fw, img, nil); err != nil {
|
}
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
fw.Close()
|
|
||||||
|
|
||||||
thumb := resize.Thumbnail(300, 185, img, resize.Lanczos3)
|
thumb := resize.Thumbnail(300, 185, img, resize.Lanczos3)
|
||||||
|
|
||||||
// Save thumbnail
|
// Save thumbnail
|
||||||
if fw, err := os.Create(filepath.Join(ThumbsDir, filename+".jpg")); err != nil {
|
fw, err := os.Create(path.Join(ThumbsDir, filename+".jpg"))
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := jpeg.Encode(fw, thumb, nil); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
fw.Close()
|
|
||||||
}
|
}
|
||||||
|
defer fw.Close()
|
||||||
|
|
||||||
|
return jpeg.Encode(fw, thumb, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
func (e *PictureExplorer) Publish(p *Picture) error {
|
||||||
|
return e.MovePicture(e.NextImgDir, e.PublishedImgDir, p.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Picture) Publish() error {
|
func (e *PictureExplorer) Unpublish(p *Picture) error {
|
||||||
npath := filepath.Join(PublishedImgDir, p.basename)
|
return e.MovePicture(e.PublishedImgDir, e.NextImgDir, p.Name)
|
||||||
if err := os.Rename(p.path, npath); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := os.Chtimes(npath, time.Now(), time.Now()); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Picture) Unpublish() error {
|
func (e *PictureExplorer) Remove(p *Picture) error {
|
||||||
if err := os.Rename(p.path, filepath.Join(NextImgDir, p.basename)); err != nil {
|
return e.DeletePicture(path.Dir(p.path), p.Name)
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Picture) Remove() error {
|
|
||||||
if err := os.Remove(p.path); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
// +build dev
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
var StaticDir string = "static/"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
mux.HandleFunc("/css/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/css")
|
|
||||||
serveStaticAsset(w, r, r.URL.Path)
|
|
||||||
})
|
|
||||||
mux.HandleFunc("/js/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/javascript")
|
|
||||||
serveStaticAsset(w, r, r.URL.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
flag.StringVar(&StaticDir, "static", StaticDir, "Directory containing static files")
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveStaticAsset(w http.ResponseWriter, r *http.Request, url string) {
|
|
||||||
if url == "index.html" {
|
|
||||||
r.URL.Path = "/"
|
|
||||||
} else {
|
|
||||||
r.URL.Path = "/" + url
|
|
||||||
}
|
|
||||||
http.FileServer(http.Dir(StaticDir)).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitizeStaticOptions() error {
|
|
||||||
StaticDir, _ = filepath.Abs(StaticDir)
|
|
||||||
if _, err := os.Stat(StaticDir); os.IsNotExist(err) {
|
|
||||||
StaticDir, _ = filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), "static"))
|
|
||||||
if _, err := os.Stat(StaticDir); os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
26
static.go
26
static.go
@ -1,36 +1,14 @@
|
|||||||
// +build !dev
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go-bindata -ignore "\\.go|\\.less" -pkg "main" -o "bindata.go" static/...
|
|
||||||
//go:generate go fmt bindata.go
|
|
||||||
|
|
||||||
const StaticDir string = "./static/"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
mux.HandleFunc("/css/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/css/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/css")
|
http.FileServer(Assets).ServeHTTP(w, r)
|
||||||
serveStaticAsset(w, r, r.URL.Path)
|
|
||||||
})
|
})
|
||||||
mux.HandleFunc("/js/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/js/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/javascript")
|
http.FileServer(Assets).ServeHTTP(w, r)
|
||||||
serveStaticAsset(w, r, r.URL.Path)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveStaticAsset(w http.ResponseWriter, r *http.Request, url string) {
|
|
||||||
if data, err := Asset(path.Join(StaticDir, url)); err != nil {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
} else {
|
|
||||||
w.Write(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitizeStaticOptions() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
7
vendor/github.com/nfnt/resize/.travis.yml
generated
vendored
7
vendor/github.com/nfnt/resize/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- tip
|
|
13
vendor/github.com/nfnt/resize/LICENSE
generated
vendored
13
vendor/github.com/nfnt/resize/LICENSE
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
149
vendor/github.com/nfnt/resize/README.md
generated
vendored
149
vendor/github.com/nfnt/resize/README.md
generated
vendored
@ -1,149 +0,0 @@
|
|||||||
Resize
|
|
||||||
======
|
|
||||||
|
|
||||||
Image resizing for the [Go programming language](http://golang.org) with common interpolation methods.
|
|
||||||
|
|
||||||
[](https://travis-ci.org/nfnt/resize)
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get github.com/nfnt/resize
|
|
||||||
```
|
|
||||||
|
|
||||||
It's that easy!
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
This package needs at least Go 1.1. Import package with
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/nfnt/resize"
|
|
||||||
```
|
|
||||||
|
|
||||||
The resize package provides 2 functions:
|
|
||||||
|
|
||||||
* `resize.Resize` creates a scaled image with new dimensions (`width`, `height`) using the interpolation function `interp`.
|
|
||||||
If either `width` or `height` is set to 0, it will be set to an aspect ratio preserving value.
|
|
||||||
* `resize.Thumbnail` downscales an image preserving its aspect ratio to the maximum dimensions (`maxWidth`, `maxHeight`).
|
|
||||||
It will return the original image if original sizes are smaller than the provided dimensions.
|
|
||||||
|
|
||||||
```go
|
|
||||||
resize.Resize(width, height uint, img image.Image, interp resize.InterpolationFunction) image.Image
|
|
||||||
resize.Thumbnail(maxWidth, maxHeight uint, img image.Image, interp resize.InterpolationFunction) image.Image
|
|
||||||
```
|
|
||||||
|
|
||||||
The provided interpolation functions are (from fast to slow execution time)
|
|
||||||
|
|
||||||
- `NearestNeighbor`: [Nearest-neighbor interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation)
|
|
||||||
- `Bilinear`: [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation)
|
|
||||||
- `Bicubic`: [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation)
|
|
||||||
- `MitchellNetravali`: [Mitchell-Netravali interpolation](http://dl.acm.org/citation.cfm?id=378514)
|
|
||||||
- `Lanczos2`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=2
|
|
||||||
- `Lanczos3`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=3
|
|
||||||
|
|
||||||
Which of these methods gives the best results depends on your use case.
|
|
||||||
|
|
||||||
Sample usage:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nfnt/resize"
|
|
||||||
"image/jpeg"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// open "test.jpg"
|
|
||||||
file, err := os.Open("test.jpg")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode jpeg into image.Image
|
|
||||||
img, err := jpeg.Decode(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
// resize to width 1000 using Lanczos resampling
|
|
||||||
// and preserve aspect ratio
|
|
||||||
m := resize.Resize(1000, 0, img, resize.Lanczos3)
|
|
||||||
|
|
||||||
out, err := os.Create("test_resized.jpg")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
// write new image to file
|
|
||||||
jpeg.Encode(out, m, nil)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Caveats
|
|
||||||
-------
|
|
||||||
|
|
||||||
* Optimized access routines are used for `image.RGBA`, `image.NRGBA`, `image.RGBA64`, `image.NRGBA64`, `image.YCbCr`, `image.Gray`, and `image.Gray16` types. All other image types are accessed in a generic way that will result in slow processing speed.
|
|
||||||
* JPEG images are stored in `image.YCbCr`. This image format stores data in a way that will decrease processing speed. A resize may be up to 2 times slower than with `image.RGBA`.
|
|
||||||
|
|
||||||
|
|
||||||
Downsizing Samples
|
|
||||||
-------
|
|
||||||
|
|
||||||
Downsizing is not as simple as it might look like. Images have to be filtered before they are scaled down, otherwise aliasing might occur.
|
|
||||||
Filtering is highly subjective: Applying too much will blur the whole image, too little will make aliasing become apparent.
|
|
||||||
Resize tries to provide sane defaults that should suffice in most cases.
|
|
||||||
|
|
||||||
### Artificial sample
|
|
||||||
|
|
||||||
Original image
|
|
||||||

|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_NearestNeighbor.png" /><br>Nearest-Neighbor</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_Bilinear.png" /><br>Bilinear</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_Bicubic.png" /><br>Bicubic</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_MitchellNetravali.png" /><br>Mitchell-Netravali</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_Lanczos2.png" /><br>Lanczos2</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/rings_300_Lanczos3.png" /><br>Lanczos3</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### Real-Life sample
|
|
||||||
|
|
||||||
Original image
|
|
||||||

|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_NearestNeighbor.png" /><br>Nearest-Neighbor</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bilinear.png" /><br>Bilinear</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bicubic.png" /><br>Bicubic</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_MitchellNetravali.png" /><br>Mitchell-Netravali</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos2.png" /><br>Lanczos2</th>
|
|
||||||
<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos3.png" /><br>Lanczos3</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
Copyright (c) 2012 Jan Schlicht <janschlicht@gmail.com>
|
|
||||||
Resize is released under a MIT style license.
|
|
438
vendor/github.com/nfnt/resize/converter.go
generated
vendored
438
vendor/github.com/nfnt/resize/converter.go
generated
vendored
@ -1,438 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import "image"
|
|
||||||
|
|
||||||
// Keep value in [0,255] range.
|
|
||||||
func clampUint8(in int32) uint8 {
|
|
||||||
// casting a negative int to an uint will result in an overflown
|
|
||||||
// large uint. this behavior will be exploited here and in other functions
|
|
||||||
// to achieve a higher performance.
|
|
||||||
if uint32(in) < 256 {
|
|
||||||
return uint8(in)
|
|
||||||
}
|
|
||||||
if in > 255 {
|
|
||||||
return 255
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep value in [0,65535] range.
|
|
||||||
func clampUint16(in int64) uint16 {
|
|
||||||
if uint64(in) < 65536 {
|
|
||||||
return uint16(in)
|
|
||||||
}
|
|
||||||
if in > 65535 {
|
|
||||||
return 65535
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]int64
|
|
||||||
var sum int64
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case xi < 0:
|
|
||||||
xi = 0
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = maxX
|
|
||||||
}
|
|
||||||
|
|
||||||
r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
|
|
||||||
|
|
||||||
rgba[0] += int64(coeff) * int64(r)
|
|
||||||
rgba[1] += int64(coeff) * int64(g)
|
|
||||||
rgba[2] += int64(coeff) * int64(b)
|
|
||||||
rgba[3] += int64(coeff) * int64(a)
|
|
||||||
sum += int64(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
|
|
||||||
value := clampUint16(rgba[0] / sum)
|
|
||||||
out.Pix[offset+0] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+1] = uint8(value)
|
|
||||||
value = clampUint16(rgba[1] / sum)
|
|
||||||
out.Pix[offset+2] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+3] = uint8(value)
|
|
||||||
value = clampUint16(rgba[2] / sum)
|
|
||||||
out.Pix[offset+4] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+5] = uint8(value)
|
|
||||||
value = clampUint16(rgba[3] / sum)
|
|
||||||
out.Pix[offset+6] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]int32
|
|
||||||
var sum int32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 4
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 4 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rgba[0] += int32(coeff) * int32(row[xi+0])
|
|
||||||
rgba[1] += int32(coeff) * int32(row[xi+1])
|
|
||||||
rgba[2] += int32(coeff) * int32(row[xi+2])
|
|
||||||
rgba[3] += int32(coeff) * int32(row[xi+3])
|
|
||||||
sum += int32(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
|
||||||
|
|
||||||
out.Pix[xo+0] = clampUint8(rgba[0] / sum)
|
|
||||||
out.Pix[xo+1] = clampUint8(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = clampUint8(rgba[2] / sum)
|
|
||||||
out.Pix[xo+3] = clampUint8(rgba[3] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeNRGBA(in *image.NRGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]int32
|
|
||||||
var sum int32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 4
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 4 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rgba[0] += int32(coeff) * int32(row[xi+0])
|
|
||||||
rgba[1] += int32(coeff) * int32(row[xi+1])
|
|
||||||
rgba[2] += int32(coeff) * int32(row[xi+2])
|
|
||||||
rgba[3] += int32(coeff) * int32(row[xi+3])
|
|
||||||
sum += int32(coeff)
|
|
||||||
|
|
||||||
// Forward alpha-premultiplication
|
|
||||||
a := int32(row[xi+3])
|
|
||||||
rgba[0] *= a
|
|
||||||
rgba[0] /= 0xff
|
|
||||||
rgba[1] *= a
|
|
||||||
rgba[1] /= 0xff
|
|
||||||
rgba[2] *= a
|
|
||||||
rgba[2] /= 0xff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
|
||||||
|
|
||||||
out.Pix[xo+0] = clampUint8(rgba[0] / sum)
|
|
||||||
out.Pix[xo+1] = clampUint8(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = clampUint8(rgba[2] / sum)
|
|
||||||
out.Pix[xo+3] = clampUint8(rgba[3] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]int64
|
|
||||||
var sum int64
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 8
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 8 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rgba[0] += int64(coeff) * (int64(row[xi+0])<<8 | int64(row[xi+1]))
|
|
||||||
rgba[1] += int64(coeff) * (int64(row[xi+2])<<8 | int64(row[xi+3]))
|
|
||||||
rgba[2] += int64(coeff) * (int64(row[xi+4])<<8 | int64(row[xi+5]))
|
|
||||||
rgba[3] += int64(coeff) * (int64(row[xi+6])<<8 | int64(row[xi+7]))
|
|
||||||
sum += int64(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
|
|
||||||
value := clampUint16(rgba[0] / sum)
|
|
||||||
out.Pix[xo+0] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+1] = uint8(value)
|
|
||||||
value = clampUint16(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+3] = uint8(value)
|
|
||||||
value = clampUint16(rgba[2] / sum)
|
|
||||||
out.Pix[xo+4] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+5] = uint8(value)
|
|
||||||
value = clampUint16(rgba[3] / sum)
|
|
||||||
out.Pix[xo+6] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeNRGBA64(in *image.NRGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]int64
|
|
||||||
var sum int64
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 8
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 8 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
|
|
||||||
rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
|
|
||||||
rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
|
|
||||||
rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(row[xi+7]))
|
|
||||||
sum += int64(coeff)
|
|
||||||
|
|
||||||
// Forward alpha-premultiplication
|
|
||||||
a := int64(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
|
|
||||||
rgba[0] *= a
|
|
||||||
rgba[0] /= 0xffff
|
|
||||||
rgba[1] *= a
|
|
||||||
rgba[1] /= 0xffff
|
|
||||||
rgba[2] *= a
|
|
||||||
rgba[2] /= 0xffff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
|
|
||||||
value := clampUint16(rgba[0] / sum)
|
|
||||||
out.Pix[xo+0] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+1] = uint8(value)
|
|
||||||
value = clampUint16(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+3] = uint8(value)
|
|
||||||
value = clampUint16(rgba[2] / sum)
|
|
||||||
out.Pix[xo+4] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+5] = uint8(value)
|
|
||||||
value = clampUint16(rgba[3] / sum)
|
|
||||||
out.Pix[xo+6] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var gray int32
|
|
||||||
var sum int32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case xi < 0:
|
|
||||||
xi = 0
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = maxX
|
|
||||||
}
|
|
||||||
gray += int32(coeff) * int32(row[xi])
|
|
||||||
sum += int32(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
|
|
||||||
out.Pix[offset] = clampUint8(gray / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var gray int64
|
|
||||||
var sum int64
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 2
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 2 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
|
|
||||||
sum += int64(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
|
|
||||||
value := clampUint16(gray / sum)
|
|
||||||
out.Pix[offset+0] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+1] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var p [3]int32
|
|
||||||
var sum int32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
coeff := coeffs[ci+i]
|
|
||||||
if coeff != 0 {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 3
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 3 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
p[0] += int32(coeff) * int32(row[xi+0])
|
|
||||||
p[1] += int32(coeff) * int32(row[xi+1])
|
|
||||||
p[2] += int32(coeff) * int32(row[xi+2])
|
|
||||||
sum += int32(coeff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
|
|
||||||
out.Pix[xo+0] = clampUint8(p[0] / sum)
|
|
||||||
out.Pix[xo+1] = clampUint8(p[1] / sum)
|
|
||||||
out.Pix[xo+2] = clampUint8(p[2] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var p [3]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 3
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 3 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
p[0] += float32(row[xi+0])
|
|
||||||
p[1] += float32(row[xi+1])
|
|
||||||
p[2] += float32(row[xi+2])
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
|
|
||||||
out.Pix[xo+0] = floatToUint8(p[0] / sum)
|
|
||||||
out.Pix[xo+1] = floatToUint8(p[1] / sum)
|
|
||||||
out.Pix[xo+2] = floatToUint8(p[2] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
43
vendor/github.com/nfnt/resize/converter_test.go
generated
vendored
43
vendor/github.com/nfnt/resize/converter_test.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_ClampUint8(t *testing.T) {
|
|
||||||
var testData = []struct {
|
|
||||||
in int32
|
|
||||||
expected uint8
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{255, 255},
|
|
||||||
{128, 128},
|
|
||||||
{-2, 0},
|
|
||||||
{256, 255},
|
|
||||||
}
|
|
||||||
for _, test := range testData {
|
|
||||||
actual := clampUint8(test.in)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ClampUint16(t *testing.T) {
|
|
||||||
var testData = []struct {
|
|
||||||
in int64
|
|
||||||
expected uint16
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{65535, 65535},
|
|
||||||
{128, 128},
|
|
||||||
{-2, 0},
|
|
||||||
{65536, 65535},
|
|
||||||
}
|
|
||||||
for _, test := range testData {
|
|
||||||
actual := clampUint16(test.in)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
143
vendor/github.com/nfnt/resize/filters.go
generated
vendored
143
vendor/github.com/nfnt/resize/filters.go
generated
vendored
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
func nearest(in float64) float64 {
|
|
||||||
if in >= -0.5 && in < 0.5 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func linear(in float64) float64 {
|
|
||||||
in = math.Abs(in)
|
|
||||||
if in <= 1 {
|
|
||||||
return 1 - in
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func cubic(in float64) float64 {
|
|
||||||
in = math.Abs(in)
|
|
||||||
if in <= 1 {
|
|
||||||
return in*in*(1.5*in-2.5) + 1.0
|
|
||||||
}
|
|
||||||
if in <= 2 {
|
|
||||||
return in*(in*(2.5-0.5*in)-4.0) + 2.0
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func mitchellnetravali(in float64) float64 {
|
|
||||||
in = math.Abs(in)
|
|
||||||
if in <= 1 {
|
|
||||||
return (7.0*in*in*in - 12.0*in*in + 5.33333333333) * 0.16666666666
|
|
||||||
}
|
|
||||||
if in <= 2 {
|
|
||||||
return (-2.33333333333*in*in*in + 12.0*in*in - 20.0*in + 10.6666666667) * 0.16666666666
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func sinc(x float64) float64 {
|
|
||||||
x = math.Abs(x) * math.Pi
|
|
||||||
if x >= 1.220703e-4 {
|
|
||||||
return math.Sin(x) / x
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func lanczos2(in float64) float64 {
|
|
||||||
if in > -2 && in < 2 {
|
|
||||||
return sinc(in) * sinc(in*0.5)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lanczos3(in float64) float64 {
|
|
||||||
if in > -3 && in < 3 {
|
|
||||||
return sinc(in) * sinc(in*0.3333333333333333)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// range [-256,256]
|
|
||||||
func createWeights8(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int16, []int, int) {
|
|
||||||
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
|
|
||||||
filterFactor := math.Min(1./(blur*scale), 1)
|
|
||||||
|
|
||||||
coeffs := make([]int16, dy*filterLength)
|
|
||||||
start := make([]int, dy)
|
|
||||||
for y := 0; y < dy; y++ {
|
|
||||||
interpX := scale*(float64(y)+0.5) - 0.5
|
|
||||||
start[y] = int(interpX) - filterLength/2 + 1
|
|
||||||
interpX -= float64(start[y])
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
in := (interpX - float64(i)) * filterFactor
|
|
||||||
coeffs[y*filterLength+i] = int16(kernel(in) * 256)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coeffs, start, filterLength
|
|
||||||
}
|
|
||||||
|
|
||||||
// range [-65536,65536]
|
|
||||||
func createWeights16(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int32, []int, int) {
|
|
||||||
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
|
|
||||||
filterFactor := math.Min(1./(blur*scale), 1)
|
|
||||||
|
|
||||||
coeffs := make([]int32, dy*filterLength)
|
|
||||||
start := make([]int, dy)
|
|
||||||
for y := 0; y < dy; y++ {
|
|
||||||
interpX := scale*(float64(y)+0.5) - 0.5
|
|
||||||
start[y] = int(interpX) - filterLength/2 + 1
|
|
||||||
interpX -= float64(start[y])
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
in := (interpX - float64(i)) * filterFactor
|
|
||||||
coeffs[y*filterLength+i] = int32(kernel(in) * 65536)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coeffs, start, filterLength
|
|
||||||
}
|
|
||||||
|
|
||||||
func createWeightsNearest(dy, filterLength int, blur, scale float64) ([]bool, []int, int) {
|
|
||||||
filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
|
|
||||||
filterFactor := math.Min(1./(blur*scale), 1)
|
|
||||||
|
|
||||||
coeffs := make([]bool, dy*filterLength)
|
|
||||||
start := make([]int, dy)
|
|
||||||
for y := 0; y < dy; y++ {
|
|
||||||
interpX := scale*(float64(y)+0.5) - 0.5
|
|
||||||
start[y] = int(interpX) - filterLength/2 + 1
|
|
||||||
interpX -= float64(start[y])
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
in := (interpX - float64(i)) * filterFactor
|
|
||||||
if in >= -0.5 && in < 0.5 {
|
|
||||||
coeffs[y*filterLength+i] = true
|
|
||||||
} else {
|
|
||||||
coeffs[y*filterLength+i] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coeffs, start, filterLength
|
|
||||||
}
|
|
318
vendor/github.com/nfnt/resize/nearest.go
generated
vendored
318
vendor/github.com/nfnt/resize/nearest.go
generated
vendored
@ -1,318 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import "image"
|
|
||||||
|
|
||||||
func floatToUint8(x float32) uint8 {
|
|
||||||
// Nearest-neighbor values are always
|
|
||||||
// positive no need to check lower-bound.
|
|
||||||
if x > 0xfe {
|
|
||||||
return 0xff
|
|
||||||
}
|
|
||||||
return uint8(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func floatToUint16(x float32) uint16 {
|
|
||||||
if x > 0xfffe {
|
|
||||||
return 0xffff
|
|
||||||
}
|
|
||||||
return uint16(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case xi < 0:
|
|
||||||
xi = 0
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = maxX
|
|
||||||
}
|
|
||||||
r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
|
|
||||||
rgba[0] += float32(r)
|
|
||||||
rgba[1] += float32(g)
|
|
||||||
rgba[2] += float32(b)
|
|
||||||
rgba[3] += float32(a)
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
value := floatToUint16(rgba[0] / sum)
|
|
||||||
out.Pix[offset+0] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+1] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[1] / sum)
|
|
||||||
out.Pix[offset+2] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+3] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[2] / sum)
|
|
||||||
out.Pix[offset+4] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+5] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[3] / sum)
|
|
||||||
out.Pix[offset+6] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 4
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 4 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
rgba[0] += float32(row[xi+0])
|
|
||||||
rgba[1] += float32(row[xi+1])
|
|
||||||
rgba[2] += float32(row[xi+2])
|
|
||||||
rgba[3] += float32(row[xi+3])
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
|
||||||
out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
|
|
||||||
out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
|
|
||||||
out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 4
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 4 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
rgba[0] += float32(row[xi+0])
|
|
||||||
rgba[1] += float32(row[xi+1])
|
|
||||||
rgba[2] += float32(row[xi+2])
|
|
||||||
rgba[3] += float32(row[xi+3])
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
|
|
||||||
out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
|
|
||||||
out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
|
|
||||||
out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 8
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 8 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
|
|
||||||
rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
|
|
||||||
rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
|
|
||||||
rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
value := floatToUint16(rgba[0] / sum)
|
|
||||||
out.Pix[xo+0] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+1] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+3] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[2] / sum)
|
|
||||||
out.Pix[xo+4] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+5] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[3] / sum)
|
|
||||||
out.Pix[xo+6] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var rgba [4]float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 8
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 8 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
|
|
||||||
rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
|
|
||||||
rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
|
|
||||||
rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
|
|
||||||
value := floatToUint16(rgba[0] / sum)
|
|
||||||
out.Pix[xo+0] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+1] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[1] / sum)
|
|
||||||
out.Pix[xo+2] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+3] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[2] / sum)
|
|
||||||
out.Pix[xo+4] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+5] = uint8(value)
|
|
||||||
value = floatToUint16(rgba[3] / sum)
|
|
||||||
out.Pix[xo+6] = uint8(value >> 8)
|
|
||||||
out.Pix[xo+7] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var gray float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case xi < 0:
|
|
||||||
xi = 0
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = maxX
|
|
||||||
}
|
|
||||||
gray += float32(row[xi])
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
|
|
||||||
out.Pix[offset] = floatToUint8(gray / sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) {
|
|
||||||
newBounds := out.Bounds()
|
|
||||||
maxX := in.Bounds().Dx() - 1
|
|
||||||
|
|
||||||
for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
|
|
||||||
row := in.Pix[x*in.Stride:]
|
|
||||||
for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
|
|
||||||
var gray float32
|
|
||||||
var sum float32
|
|
||||||
start := offset[y]
|
|
||||||
ci := y * filterLength
|
|
||||||
for i := 0; i < filterLength; i++ {
|
|
||||||
if coeffs[ci+i] {
|
|
||||||
xi := start + i
|
|
||||||
switch {
|
|
||||||
case uint(xi) < uint(maxX):
|
|
||||||
xi *= 2
|
|
||||||
case xi >= maxX:
|
|
||||||
xi = 2 * maxX
|
|
||||||
default:
|
|
||||||
xi = 0
|
|
||||||
}
|
|
||||||
gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
|
|
||||||
sum++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
|
|
||||||
value := floatToUint16(gray / sum)
|
|
||||||
out.Pix[offset+0] = uint8(value >> 8)
|
|
||||||
out.Pix[offset+1] = uint8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
vendor/github.com/nfnt/resize/nearest_test.go
generated
vendored
57
vendor/github.com/nfnt/resize/nearest_test.go
generated
vendored
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func Test_FloatToUint8(t *testing.T) {
|
|
||||||
var testData = []struct {
|
|
||||||
in float32
|
|
||||||
expected uint8
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{255, 255},
|
|
||||||
{128, 128},
|
|
||||||
{1, 1},
|
|
||||||
{256, 255},
|
|
||||||
}
|
|
||||||
for _, test := range testData {
|
|
||||||
actual := floatToUint8(test.in)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_FloatToUint16(t *testing.T) {
|
|
||||||
var testData = []struct {
|
|
||||||
in float32
|
|
||||||
expected uint16
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{65535, 65535},
|
|
||||||
{128, 128},
|
|
||||||
{1, 1},
|
|
||||||
{65536, 65535},
|
|
||||||
}
|
|
||||||
for _, test := range testData {
|
|
||||||
actual := floatToUint16(test.in)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
614
vendor/github.com/nfnt/resize/resize.go
generated
vendored
614
vendor/github.com/nfnt/resize/resize.go
generated
vendored
@ -1,614 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Package resize implements various image resizing methods.
|
|
||||||
//
|
|
||||||
// The package works with the Image interface described in the image package.
|
|
||||||
// Various interpolation methods are provided and multiple processors may be
|
|
||||||
// utilized in the computations.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// imgResized := resize.Resize(1000, 0, imgOld, resize.MitchellNetravali)
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An InterpolationFunction provides the parameters that describe an
|
|
||||||
// interpolation kernel. It returns the number of samples to take
|
|
||||||
// and the kernel function to use for sampling.
|
|
||||||
type InterpolationFunction int
|
|
||||||
|
|
||||||
// InterpolationFunction constants
|
|
||||||
const (
|
|
||||||
// Nearest-neighbor interpolation
|
|
||||||
NearestNeighbor InterpolationFunction = iota
|
|
||||||
// Bilinear interpolation
|
|
||||||
Bilinear
|
|
||||||
// Bicubic interpolation (with cubic hermite spline)
|
|
||||||
Bicubic
|
|
||||||
// Mitchell-Netravali interpolation
|
|
||||||
MitchellNetravali
|
|
||||||
// Lanczos interpolation (a=2)
|
|
||||||
Lanczos2
|
|
||||||
// Lanczos interpolation (a=3)
|
|
||||||
Lanczos3
|
|
||||||
)
|
|
||||||
|
|
||||||
// kernal, returns an InterpolationFunctions taps and kernel.
|
|
||||||
func (i InterpolationFunction) kernel() (int, func(float64) float64) {
|
|
||||||
switch i {
|
|
||||||
case Bilinear:
|
|
||||||
return 2, linear
|
|
||||||
case Bicubic:
|
|
||||||
return 4, cubic
|
|
||||||
case MitchellNetravali:
|
|
||||||
return 4, mitchellnetravali
|
|
||||||
case Lanczos2:
|
|
||||||
return 4, lanczos2
|
|
||||||
case Lanczos3:
|
|
||||||
return 6, lanczos3
|
|
||||||
default:
|
|
||||||
// Default to NearestNeighbor.
|
|
||||||
return 2, nearest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// values <1 will sharpen the image
|
|
||||||
var blur = 1.0
|
|
||||||
|
|
||||||
// Resize scales an image to new width and height using the interpolation function interp.
|
|
||||||
// A new image with the given dimensions will be returned.
|
|
||||||
// If one of the parameters width or height is set to 0, its size will be calculated so that
|
|
||||||
// the aspect ratio is that of the originating image.
|
|
||||||
// The resizing algorithm uses channels for parallel computation.
|
|
||||||
func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image {
|
|
||||||
scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
|
|
||||||
if width == 0 {
|
|
||||||
width = uint(0.7 + float64(img.Bounds().Dx())/scaleX)
|
|
||||||
}
|
|
||||||
if height == 0 {
|
|
||||||
height = uint(0.7 + float64(img.Bounds().Dy())/scaleY)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trivial case: return input image
|
|
||||||
if int(width) == img.Bounds().Dx() && int(height) == img.Bounds().Dy() {
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
if interp == NearestNeighbor {
|
|
||||||
return resizeNearest(width, height, scaleX, scaleY, img, interp)
|
|
||||||
}
|
|
||||||
|
|
||||||
taps, kernel := interp.kernel()
|
|
||||||
cpus := runtime.GOMAXPROCS(0)
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
// Generic access to image.Image is slow in tight loops.
|
|
||||||
// The optimal access has to be determined from the concrete image type.
|
|
||||||
switch input := img.(type) {
|
|
||||||
case *image.RGBA:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.NRGBA:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
|
|
||||||
case *image.YCbCr:
|
|
||||||
// 8-bit precision
|
|
||||||
// accessing the YCbCr arrays in a tight loop is slow.
|
|
||||||
// converting the image to ycc increases performance by 2x.
|
|
||||||
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
|
|
||||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
|
|
||||||
|
|
||||||
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
in := imageYCbCrToYCC(input)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*ycc)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*ycc)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result.YCbCr()
|
|
||||||
case *image.RGBA64:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.NRGBA64:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.Gray:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.Gray)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeGray(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.Gray)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeGray(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.Gray16:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.Gray16)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeGray16(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.Gray16)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
default:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image {
|
|
||||||
taps, _ := interp.kernel()
|
|
||||||
cpus := runtime.GOMAXPROCS(0)
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
switch input := img.(type) {
|
|
||||||
case *image.RGBA:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.NRGBA:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.NRGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.NRGBA)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.YCbCr:
|
|
||||||
// 8-bit precision
|
|
||||||
// accessing the YCbCr arrays in a tight loop is slow.
|
|
||||||
// converting the image to ycc increases performance by 2x.
|
|
||||||
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
|
|
||||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
|
|
||||||
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
in := imageYCbCrToYCC(input)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*ycc)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*ycc)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result.YCbCr()
|
|
||||||
case *image.RGBA64:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.NRGBA64:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.NRGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.Gray:
|
|
||||||
// 8-bit precision
|
|
||||||
temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.Gray)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestGray(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.Gray)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestGray(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
case *image.Gray16:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.Gray16)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestGray16(input, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.Gray16)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
default:
|
|
||||||
// 16-bit precision
|
|
||||||
temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
|
|
||||||
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
||||||
|
|
||||||
// horizontal filter, results in transposed temporary image
|
|
||||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// horizontal filter on transposed image, result is not transposed
|
|
||||||
coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
|
|
||||||
wg.Add(cpus)
|
|
||||||
for i := 0; i < cpus; i++ {
|
|
||||||
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculates scaling factors using old and new image dimensions.
|
|
||||||
func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) {
|
|
||||||
if width == 0 {
|
|
||||||
if height == 0 {
|
|
||||||
scaleX = 1.0
|
|
||||||
scaleY = 1.0
|
|
||||||
} else {
|
|
||||||
scaleY = oldHeight / float64(height)
|
|
||||||
scaleX = scaleY
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scaleX = oldWidth / float64(width)
|
|
||||||
if height == 0 {
|
|
||||||
scaleY = scaleX
|
|
||||||
} else {
|
|
||||||
scaleY = oldHeight / float64(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type imageWithSubImage interface {
|
|
||||||
image.Image
|
|
||||||
SubImage(image.Rectangle) image.Image
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeSlice(img imageWithSubImage, i, n int) image.Image {
|
|
||||||
return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n))
|
|
||||||
}
|
|
330
vendor/github.com/nfnt/resize/resize_test.go
generated
vendored
330
vendor/github.com/nfnt/resize/resize_test.go
generated
vendored
@ -1,330 +0,0 @@
|
|||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var img = image.NewGray16(image.Rect(0, 0, 3, 3))
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
img.Set(1, 1, color.White)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Param1(t *testing.T) {
|
|
||||||
m := Resize(0, 0, img, NearestNeighbor)
|
|
||||||
if m.Bounds() != img.Bounds() {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Param2(t *testing.T) {
|
|
||||||
m := Resize(100, 0, img, NearestNeighbor)
|
|
||||||
if m.Bounds() != image.Rect(0, 0, 100, 100) {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ZeroImg(t *testing.T) {
|
|
||||||
zeroImg := image.NewGray16(image.Rect(0, 0, 0, 0))
|
|
||||||
|
|
||||||
m := Resize(0, 0, zeroImg, NearestNeighbor)
|
|
||||||
if m.Bounds() != zeroImg.Bounds() {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_CorrectResize(t *testing.T) {
|
|
||||||
zeroImg := image.NewGray16(image.Rect(0, 0, 256, 256))
|
|
||||||
|
|
||||||
m := Resize(60, 0, zeroImg, NearestNeighbor)
|
|
||||||
if m.Bounds() != image.Rect(0, 0, 60, 60) {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithRGBA(t *testing.T) {
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetRGBA(x, y, color.RGBA{0x80, 0x80, 0x80, 0xFF})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.RGBA)
|
|
||||||
if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithNRGBA(t *testing.T) {
|
|
||||||
img := image.NewNRGBA(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetNRGBA(x, y, color.NRGBA{0x80, 0x80, 0x80, 0xFF})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.RGBA)
|
|
||||||
if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithRGBA64(t *testing.T) {
|
|
||||||
img := image.NewRGBA64(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetRGBA64(x, y, color.RGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.RGBA64)
|
|
||||||
if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithNRGBA64(t *testing.T) {
|
|
||||||
img := image.NewNRGBA64(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetNRGBA64(x, y, color.NRGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.RGBA64)
|
|
||||||
if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithGray(t *testing.T) {
|
|
||||||
img := image.NewGray(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetGray(x, y, color.Gray{0x80})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.Gray)
|
|
||||||
if color.Y != 0x80 {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameColorWithGray16(t *testing.T) {
|
|
||||||
img := image.NewGray16(image.Rect(0, 0, 20, 20))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
||||||
img.SetGray16(x, y, color.Gray16{0x8000})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out := Resize(10, 10, img, Lanczos3)
|
|
||||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
|
||||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
|
||||||
color := out.At(x, y).(color.Gray16)
|
|
||||||
if color.Y != 0x8000 {
|
|
||||||
t.Errorf("%+v", color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Bounds(t *testing.T) {
|
|
||||||
img := image.NewRGBA(image.Rect(20, 10, 200, 99))
|
|
||||||
out := Resize(80, 80, img, Lanczos2)
|
|
||||||
out.At(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SameSizeReturnsOriginal(t *testing.T) {
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 10, 10))
|
|
||||||
out := Resize(0, 0, img, Lanczos2)
|
|
||||||
|
|
||||||
if img != out {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
out = Resize(10, 10, img, Lanczos2)
|
|
||||||
|
|
||||||
if img != out {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_PixelCoordinates(t *testing.T) {
|
|
||||||
checkers := image.NewGray(image.Rect(0, 0, 4, 4))
|
|
||||||
checkers.Pix = []uint8{
|
|
||||||
255, 0, 255, 0,
|
|
||||||
0, 255, 0, 255,
|
|
||||||
255, 0, 255, 0,
|
|
||||||
0, 255, 0, 255,
|
|
||||||
}
|
|
||||||
|
|
||||||
resized := Resize(12, 12, checkers, NearestNeighbor).(*image.Gray)
|
|
||||||
|
|
||||||
if resized.Pix[0] != 255 || resized.Pix[1] != 255 || resized.Pix[2] != 255 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
if resized.Pix[3] != 0 || resized.Pix[4] != 0 || resized.Pix[5] != 0 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ResizeWithPremultipliedAlpha(t *testing.T) {
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 1, 4))
|
|
||||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
||||||
// 0x80 = 0.5 * 0xFF.
|
|
||||||
img.SetRGBA(0, y, color.RGBA{0x80, 0x80, 0x80, 0x80})
|
|
||||||
}
|
|
||||||
|
|
||||||
out := Resize(1, 2, img, MitchellNetravali)
|
|
||||||
|
|
||||||
outputColor := out.At(0, 0).(color.RGBA)
|
|
||||||
if outputColor.R != 0x80 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ResizeWithTranslucentColor(t *testing.T) {
|
|
||||||
img := image.NewNRGBA(image.Rect(0, 0, 1, 2))
|
|
||||||
|
|
||||||
// Set the pixel colors to an "invisible green" and white.
|
|
||||||
// After resizing, the green shouldn't be visible.
|
|
||||||
img.SetNRGBA(0, 0, color.NRGBA{0x00, 0xFF, 0x00, 0x00})
|
|
||||||
img.SetNRGBA(0, 1, color.NRGBA{0x00, 0x00, 0x00, 0xFF})
|
|
||||||
|
|
||||||
out := Resize(1, 1, img, Bilinear)
|
|
||||||
|
|
||||||
_, g, _, _ := out.At(0, 0).RGBA()
|
|
||||||
if g != 0x00 {
|
|
||||||
t.Errorf("%+v", g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Use a small image size for benchmarks. We don't want memory performance
|
|
||||||
// to affect the benchmark results.
|
|
||||||
benchMaxX = 250
|
|
||||||
benchMaxY = 250
|
|
||||||
|
|
||||||
// Resize values near the original size require increase the amount of time
|
|
||||||
// resize spends converting the image.
|
|
||||||
benchWidth = 200
|
|
||||||
benchHeight = 200
|
|
||||||
)
|
|
||||||
|
|
||||||
func benchRGBA(b *testing.B, interp InterpolationFunction) {
|
|
||||||
m := image.NewRGBA(image.Rect(0, 0, benchMaxX, benchMaxY))
|
|
||||||
// Initialize m's pixels to create a non-uniform image.
|
|
||||||
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
|
|
||||||
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
|
|
||||||
i := m.PixOffset(x, y)
|
|
||||||
m.Pix[i+0] = uint8(y + 4*x)
|
|
||||||
m.Pix[i+1] = uint8(y + 4*x)
|
|
||||||
m.Pix[i+2] = uint8(y + 4*x)
|
|
||||||
m.Pix[i+3] = uint8(4*y + x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var out image.Image
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
out = Resize(benchWidth, benchHeight, m, interp)
|
|
||||||
}
|
|
||||||
out.At(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The names of some interpolation functions are truncated so that the columns
|
|
||||||
// of 'go test -bench' line up.
|
|
||||||
func Benchmark_Nearest_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, NearestNeighbor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Bilinear_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, Bilinear)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Bicubic_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, Bicubic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Mitchell_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, MitchellNetravali)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Lanczos2_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, Lanczos2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Lanczos3_RGBA(b *testing.B) {
|
|
||||||
benchRGBA(b, Lanczos3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchYCbCr(b *testing.B, interp InterpolationFunction) {
|
|
||||||
m := image.NewYCbCr(image.Rect(0, 0, benchMaxX, benchMaxY), image.YCbCrSubsampleRatio422)
|
|
||||||
// Initialize m's pixels to create a non-uniform image.
|
|
||||||
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
|
|
||||||
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
|
|
||||||
yi := m.YOffset(x, y)
|
|
||||||
ci := m.COffset(x, y)
|
|
||||||
m.Y[yi] = uint8(16*y + x)
|
|
||||||
m.Cb[ci] = uint8(y + 16*x)
|
|
||||||
m.Cr[ci] = uint8(y + 16*x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var out image.Image
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
out = Resize(benchWidth, benchHeight, m, interp)
|
|
||||||
}
|
|
||||||
out.At(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Nearest_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, NearestNeighbor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Bilinear_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, Bilinear)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Bicubic_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, Bicubic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Mitchell_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, MitchellNetravali)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Lanczos2_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, Lanczos2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_Lanczos3_YCC(b *testing.B) {
|
|
||||||
benchYCbCr(b, Lanczos3)
|
|
||||||
}
|
|
55
vendor/github.com/nfnt/resize/thumbnail.go
generated
vendored
55
vendor/github.com/nfnt/resize/thumbnail.go
generated
vendored
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Thumbnail will downscale provided image to max width and height preserving
|
|
||||||
// original aspect ratio and using the interpolation function interp.
|
|
||||||
// It will return original image, without processing it, if original sizes
|
|
||||||
// are already smaller than provided constraints.
|
|
||||||
func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image {
|
|
||||||
origBounds := img.Bounds()
|
|
||||||
origWidth := uint(origBounds.Dx())
|
|
||||||
origHeight := uint(origBounds.Dy())
|
|
||||||
newWidth, newHeight := origWidth, origHeight
|
|
||||||
|
|
||||||
// Return original image if it have same or smaller size as constraints
|
|
||||||
if maxWidth >= origWidth && maxHeight >= origHeight {
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preserve aspect ratio
|
|
||||||
if origWidth > maxWidth {
|
|
||||||
newHeight = uint(origHeight * maxWidth / origWidth)
|
|
||||||
if newHeight < 1 {
|
|
||||||
newHeight = 1
|
|
||||||
}
|
|
||||||
newWidth = maxWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
if newHeight > maxHeight {
|
|
||||||
newWidth = uint(newWidth * maxHeight / newHeight)
|
|
||||||
if newWidth < 1 {
|
|
||||||
newWidth = 1
|
|
||||||
}
|
|
||||||
newHeight = maxHeight
|
|
||||||
}
|
|
||||||
return Resize(newWidth, newHeight, img, interp)
|
|
||||||
}
|
|
47
vendor/github.com/nfnt/resize/thumbnail_test.go
generated
vendored
47
vendor/github.com/nfnt/resize/thumbnail_test.go
generated
vendored
@ -1,47 +0,0 @@
|
|||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
}
|
|
||||||
|
|
||||||
var thumbnailTests = []struct {
|
|
||||||
origWidth int
|
|
||||||
origHeight int
|
|
||||||
maxWidth uint
|
|
||||||
maxHeight uint
|
|
||||||
expectedWidth uint
|
|
||||||
expectedHeight uint
|
|
||||||
}{
|
|
||||||
{5, 5, 10, 10, 5, 5},
|
|
||||||
{10, 10, 5, 5, 5, 5},
|
|
||||||
{10, 50, 10, 10, 2, 10},
|
|
||||||
{50, 10, 10, 10, 10, 2},
|
|
||||||
{50, 100, 60, 90, 45, 90},
|
|
||||||
{120, 100, 60, 90, 60, 50},
|
|
||||||
{200, 250, 200, 150, 120, 150},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestThumbnail(t *testing.T) {
|
|
||||||
for i, tt := range thumbnailTests {
|
|
||||||
img := image.NewGray16(image.Rect(0, 0, tt.origWidth, tt.origHeight))
|
|
||||||
|
|
||||||
outImg := Thumbnail(tt.maxWidth, tt.maxHeight, img, NearestNeighbor)
|
|
||||||
|
|
||||||
newWidth := uint(outImg.Bounds().Dx())
|
|
||||||
newHeight := uint(outImg.Bounds().Dy())
|
|
||||||
if newWidth != tt.expectedWidth ||
|
|
||||||
newHeight != tt.expectedHeight {
|
|
||||||
t.Errorf("%d. Thumbnail(%v, %v, img, NearestNeighbor) => "+
|
|
||||||
"width: %v, height: %v, want width: %v, height: %v",
|
|
||||||
i, tt.maxWidth, tt.maxHeight,
|
|
||||||
newWidth, newHeight, tt.expectedWidth, tt.expectedHeight,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
227
vendor/github.com/nfnt/resize/ycc.go
generated
vendored
227
vendor/github.com/nfnt/resize/ycc.go
generated
vendored
@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a
|
|
||||||
// single slice to increase resizing performance.
|
|
||||||
type ycc struct {
|
|
||||||
// Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at
|
|
||||||
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
|
|
||||||
Pix []uint8
|
|
||||||
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
|
|
||||||
Stride int
|
|
||||||
// Rect is the image's bounds.
|
|
||||||
Rect image.Rectangle
|
|
||||||
// SubsampleRatio is the subsample ratio of the original YCbCr image.
|
|
||||||
SubsampleRatio image.YCbCrSubsampleRatio
|
|
||||||
}
|
|
||||||
|
|
||||||
// PixOffset returns the index of the first element of Pix that corresponds to
|
|
||||||
// the pixel at (x, y).
|
|
||||||
func (p *ycc) PixOffset(x, y int) int {
|
|
||||||
return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ycc) Bounds() image.Rectangle {
|
|
||||||
return p.Rect
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ycc) ColorModel() color.Model {
|
|
||||||
return color.YCbCrModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ycc) At(x, y int) color.Color {
|
|
||||||
if !(image.Point{x, y}.In(p.Rect)) {
|
|
||||||
return color.YCbCr{}
|
|
||||||
}
|
|
||||||
i := p.PixOffset(x, y)
|
|
||||||
return color.YCbCr{
|
|
||||||
p.Pix[i+0],
|
|
||||||
p.Pix[i+1],
|
|
||||||
p.Pix[i+2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ycc) Opaque() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubImage returns an image representing the portion of the image p visible
|
|
||||||
// through r. The returned value shares pixels with the original image.
|
|
||||||
func (p *ycc) SubImage(r image.Rectangle) image.Image {
|
|
||||||
r = r.Intersect(p.Rect)
|
|
||||||
if r.Empty() {
|
|
||||||
return &ycc{SubsampleRatio: p.SubsampleRatio}
|
|
||||||
}
|
|
||||||
i := p.PixOffset(r.Min.X, r.Min.Y)
|
|
||||||
return &ycc{
|
|
||||||
Pix: p.Pix[i:],
|
|
||||||
Stride: p.Stride,
|
|
||||||
Rect: r,
|
|
||||||
SubsampleRatio: p.SubsampleRatio,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newYCC returns a new ycc with the given bounds and subsample ratio.
|
|
||||||
func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc {
|
|
||||||
w, h := r.Dx(), r.Dy()
|
|
||||||
buf := make([]uint8, 3*w*h)
|
|
||||||
return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s}
|
|
||||||
}
|
|
||||||
|
|
||||||
// YCbCr converts ycc to a YCbCr image with the same subsample ratio
|
|
||||||
// as the YCbCr image that ycc was generated from.
|
|
||||||
func (p *ycc) YCbCr() *image.YCbCr {
|
|
||||||
ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
|
|
||||||
var off int
|
|
||||||
|
|
||||||
switch ycbcr.SubsampleRatio {
|
|
||||||
case image.YCbCrSubsampleRatio422:
|
|
||||||
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
||||||
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
|
|
||||||
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
||||||
xx := (x - ycbcr.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx/2
|
|
||||||
ycbcr.Y[yi] = p.Pix[off+0]
|
|
||||||
ycbcr.Cb[ci] = p.Pix[off+1]
|
|
||||||
ycbcr.Cr[ci] = p.Pix[off+2]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case image.YCbCrSubsampleRatio420:
|
|
||||||
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
||||||
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
|
|
||||||
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
||||||
xx := (x - ycbcr.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx/2
|
|
||||||
ycbcr.Y[yi] = p.Pix[off+0]
|
|
||||||
ycbcr.Cb[ci] = p.Pix[off+1]
|
|
||||||
ycbcr.Cr[ci] = p.Pix[off+2]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case image.YCbCrSubsampleRatio440:
|
|
||||||
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
||||||
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
|
|
||||||
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
||||||
xx := (x - ycbcr.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx
|
|
||||||
ycbcr.Y[yi] = p.Pix[off+0]
|
|
||||||
ycbcr.Cb[ci] = p.Pix[off+1]
|
|
||||||
ycbcr.Cr[ci] = p.Pix[off+2]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Default to 4:4:4 subsampling.
|
|
||||||
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
||||||
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
|
|
||||||
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
||||||
xx := (x - ycbcr.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx
|
|
||||||
ycbcr.Y[yi] = p.Pix[off+0]
|
|
||||||
ycbcr.Cb[ci] = p.Pix[off+1]
|
|
||||||
ycbcr.Cr[ci] = p.Pix[off+2]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ycbcr
|
|
||||||
}
|
|
||||||
|
|
||||||
// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
|
|
||||||
func imageYCbCrToYCC(in *image.YCbCr) *ycc {
|
|
||||||
w, h := in.Rect.Dx(), in.Rect.Dy()
|
|
||||||
r := image.Rect(0, 0, w, h)
|
|
||||||
buf := make([]uint8, 3*w*h)
|
|
||||||
p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio}
|
|
||||||
var off int
|
|
||||||
|
|
||||||
switch in.SubsampleRatio {
|
|
||||||
case image.YCbCrSubsampleRatio422:
|
|
||||||
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - in.Rect.Min.Y) * in.YStride
|
|
||||||
cy := (y - in.Rect.Min.Y) * in.CStride
|
|
||||||
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
||||||
xx := (x - in.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx/2
|
|
||||||
p.Pix[off+0] = in.Y[yi]
|
|
||||||
p.Pix[off+1] = in.Cb[ci]
|
|
||||||
p.Pix[off+2] = in.Cr[ci]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case image.YCbCrSubsampleRatio420:
|
|
||||||
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - in.Rect.Min.Y) * in.YStride
|
|
||||||
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
|
|
||||||
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
||||||
xx := (x - in.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx/2
|
|
||||||
p.Pix[off+0] = in.Y[yi]
|
|
||||||
p.Pix[off+1] = in.Cb[ci]
|
|
||||||
p.Pix[off+2] = in.Cr[ci]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case image.YCbCrSubsampleRatio440:
|
|
||||||
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - in.Rect.Min.Y) * in.YStride
|
|
||||||
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
|
|
||||||
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
||||||
xx := (x - in.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx
|
|
||||||
p.Pix[off+0] = in.Y[yi]
|
|
||||||
p.Pix[off+1] = in.Cb[ci]
|
|
||||||
p.Pix[off+2] = in.Cr[ci]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Default to 4:4:4 subsampling.
|
|
||||||
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
||||||
yy := (y - in.Rect.Min.Y) * in.YStride
|
|
||||||
cy := (y - in.Rect.Min.Y) * in.CStride
|
|
||||||
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
||||||
xx := (x - in.Rect.Min.X)
|
|
||||||
yi := yy + xx
|
|
||||||
ci := cy + xx
|
|
||||||
p.Pix[off+0] = in.Y[yi]
|
|
||||||
p.Pix[off+1] = in.Cb[ci]
|
|
||||||
p.Pix[off+2] = in.Cr[ci]
|
|
||||||
off += 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &p
|
|
||||||
}
|
|
214
vendor/github.com/nfnt/resize/ycc_test.go
generated
vendored
214
vendor/github.com/nfnt/resize/ycc_test.go
generated
vendored
@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
||||||
with or without fee is hereby granted, provided that the above copyright notice
|
|
||||||
and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
||||||
THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package resize
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Image interface {
|
|
||||||
image.Image
|
|
||||||
SubImage(image.Rectangle) image.Image
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImage(t *testing.T) {
|
|
||||||
testImage := []Image{
|
|
||||||
newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio420),
|
|
||||||
newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio422),
|
|
||||||
newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio440),
|
|
||||||
newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio444),
|
|
||||||
}
|
|
||||||
for _, m := range testImage {
|
|
||||||
if !image.Rect(0, 0, 10, 10).Eq(m.Bounds()) {
|
|
||||||
t.Errorf("%T: want bounds %v, got %v",
|
|
||||||
m, image.Rect(0, 0, 10, 10), m.Bounds())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m = m.SubImage(image.Rect(3, 2, 9, 8)).(Image)
|
|
||||||
if !image.Rect(3, 2, 9, 8).Eq(m.Bounds()) {
|
|
||||||
t.Errorf("%T: sub-image want bounds %v, got %v",
|
|
||||||
m, image.Rect(3, 2, 9, 8), m.Bounds())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Test that taking an empty sub-image starting at a corner does not panic.
|
|
||||||
m.SubImage(image.Rect(0, 0, 0, 0))
|
|
||||||
m.SubImage(image.Rect(10, 0, 10, 0))
|
|
||||||
m.SubImage(image.Rect(0, 10, 0, 10))
|
|
||||||
m.SubImage(image.Rect(10, 10, 10, 10))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertYCbCr(t *testing.T) {
|
|
||||||
testImage := []Image{
|
|
||||||
image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio420),
|
|
||||||
image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio422),
|
|
||||||
image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio440),
|
|
||||||
image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio444),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, img := range testImage {
|
|
||||||
m := img.(*image.YCbCr)
|
|
||||||
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
|
|
||||||
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
|
|
||||||
yi := m.YOffset(x, y)
|
|
||||||
ci := m.COffset(x, y)
|
|
||||||
m.Y[yi] = uint8(16*y + x)
|
|
||||||
m.Cb[ci] = uint8(y + 16*x)
|
|
||||||
m.Cr[ci] = uint8(y + 16*x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test conversion from YCbCr to ycc
|
|
||||||
yc := imageYCbCrToYCC(m)
|
|
||||||
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
|
|
||||||
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
|
|
||||||
ystride := 3 * (m.Rect.Max.X - m.Rect.Min.X)
|
|
||||||
xstride := 3
|
|
||||||
yi := m.YOffset(x, y)
|
|
||||||
ci := m.COffset(x, y)
|
|
||||||
si := (y * ystride) + (x * xstride)
|
|
||||||
if m.Y[yi] != yc.Pix[si] {
|
|
||||||
t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d si: %d",
|
|
||||||
m.Y[yi], yc.Pix[si], x, y, yi, si)
|
|
||||||
}
|
|
||||||
if m.Cb[ci] != yc.Pix[si+1] {
|
|
||||||
t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d si: %d",
|
|
||||||
m.Cb[ci], yc.Pix[si+1], x, y, ci, si+1)
|
|
||||||
}
|
|
||||||
if m.Cr[ci] != yc.Pix[si+2] {
|
|
||||||
t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d si: %d",
|
|
||||||
m.Cr[ci], yc.Pix[si+2], x, y, ci, si+2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test conversion from ycc back to YCbCr
|
|
||||||
ym := yc.YCbCr()
|
|
||||||
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
|
|
||||||
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
|
|
||||||
yi := m.YOffset(x, y)
|
|
||||||
ci := m.COffset(x, y)
|
|
||||||
if m.Y[yi] != ym.Y[yi] {
|
|
||||||
t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d",
|
|
||||||
m.Y[yi], ym.Y[yi], x, y, yi)
|
|
||||||
}
|
|
||||||
if m.Cb[ci] != ym.Cb[ci] {
|
|
||||||
t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d",
|
|
||||||
m.Cb[ci], ym.Cb[ci], x, y, ci)
|
|
||||||
}
|
|
||||||
if m.Cr[ci] != ym.Cr[ci] {
|
|
||||||
t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d",
|
|
||||||
m.Cr[ci], ym.Cr[ci], x, y, ci)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestYCbCr(t *testing.T) {
|
|
||||||
rects := []image.Rectangle{
|
|
||||||
image.Rect(0, 0, 16, 16),
|
|
||||||
image.Rect(1, 0, 16, 16),
|
|
||||||
image.Rect(0, 1, 16, 16),
|
|
||||||
image.Rect(1, 1, 16, 16),
|
|
||||||
image.Rect(1, 1, 15, 16),
|
|
||||||
image.Rect(1, 1, 16, 15),
|
|
||||||
image.Rect(1, 1, 15, 15),
|
|
||||||
image.Rect(2, 3, 14, 15),
|
|
||||||
image.Rect(7, 0, 7, 16),
|
|
||||||
image.Rect(0, 8, 16, 8),
|
|
||||||
image.Rect(0, 0, 10, 11),
|
|
||||||
image.Rect(5, 6, 16, 16),
|
|
||||||
image.Rect(7, 7, 8, 8),
|
|
||||||
image.Rect(7, 8, 8, 9),
|
|
||||||
image.Rect(8, 7, 9, 8),
|
|
||||||
image.Rect(8, 8, 9, 9),
|
|
||||||
image.Rect(7, 7, 17, 17),
|
|
||||||
image.Rect(8, 8, 17, 17),
|
|
||||||
image.Rect(9, 9, 17, 17),
|
|
||||||
image.Rect(10, 10, 17, 17),
|
|
||||||
}
|
|
||||||
subsampleRatios := []image.YCbCrSubsampleRatio{
|
|
||||||
image.YCbCrSubsampleRatio444,
|
|
||||||
image.YCbCrSubsampleRatio422,
|
|
||||||
image.YCbCrSubsampleRatio420,
|
|
||||||
image.YCbCrSubsampleRatio440,
|
|
||||||
}
|
|
||||||
deltas := []image.Point{
|
|
||||||
image.Pt(0, 0),
|
|
||||||
image.Pt(1000, 1001),
|
|
||||||
image.Pt(5001, -400),
|
|
||||||
image.Pt(-701, -801),
|
|
||||||
}
|
|
||||||
for _, r := range rects {
|
|
||||||
for _, subsampleRatio := range subsampleRatios {
|
|
||||||
for _, delta := range deltas {
|
|
||||||
testYCbCr(t, r, subsampleRatio, delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if testing.Short() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testYCbCr(t *testing.T, r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio, delta image.Point) {
|
|
||||||
// Create a YCbCr image m, whose bounds are r translated by (delta.X, delta.Y).
|
|
||||||
r1 := r.Add(delta)
|
|
||||||
img := image.NewYCbCr(r1, subsampleRatio)
|
|
||||||
|
|
||||||
// Initialize img's pixels. For 422 and 420 subsampling, some of the Cb and Cr elements
|
|
||||||
// will be set multiple times. That's OK. We just want to avoid a uniform image.
|
|
||||||
for y := r1.Min.Y; y < r1.Max.Y; y++ {
|
|
||||||
for x := r1.Min.X; x < r1.Max.X; x++ {
|
|
||||||
yi := img.YOffset(x, y)
|
|
||||||
ci := img.COffset(x, y)
|
|
||||||
img.Y[yi] = uint8(16*y + x)
|
|
||||||
img.Cb[ci] = uint8(y + 16*x)
|
|
||||||
img.Cr[ci] = uint8(y + 16*x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m := imageYCbCrToYCC(img)
|
|
||||||
|
|
||||||
// Make various sub-images of m.
|
|
||||||
for y0 := delta.Y + 3; y0 < delta.Y+7; y0++ {
|
|
||||||
for y1 := delta.Y + 8; y1 < delta.Y+13; y1++ {
|
|
||||||
for x0 := delta.X + 3; x0 < delta.X+7; x0++ {
|
|
||||||
for x1 := delta.X + 8; x1 < delta.X+13; x1++ {
|
|
||||||
subRect := image.Rect(x0, y0, x1, y1)
|
|
||||||
sub := m.SubImage(subRect).(*ycc)
|
|
||||||
|
|
||||||
// For each point in the sub-image's bounds, check that m.At(x, y) equals sub.At(x, y).
|
|
||||||
for y := sub.Rect.Min.Y; y < sub.Rect.Max.Y; y++ {
|
|
||||||
for x := sub.Rect.Min.X; x < sub.Rect.Max.X; x++ {
|
|
||||||
color0 := m.At(x, y).(color.YCbCr)
|
|
||||||
color1 := sub.At(x, y).(color.YCbCr)
|
|
||||||
if color0 != color1 {
|
|
||||||
t.Errorf("r=%v, subsampleRatio=%v, delta=%v, x=%d, y=%d, color0=%v, color1=%v",
|
|
||||||
r, subsampleRatio, delta, x, y, color0, color1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
7
vendor/gitlab.com/nyarla/go-crypt/.travis.yml
generated
vendored
7
vendor/gitlab.com/nyarla/go-crypt/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- tip
|
|
14
vendor/gitlab.com/nyarla/go-crypt/LICENSE
generated
vendored
14
vendor/gitlab.com/nyarla/go-crypt/LICENSE
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
Copyright (c) 2009, <iiasija>
|
|
||||||
Copyright (c) 2013-2014 Naoki OKAMURA (Nyarla) <nyarla@thotep.net>
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
42
vendor/gitlab.com/nyarla/go-crypt/README.md
generated
vendored
42
vendor/gitlab.com/nyarla/go-crypt/README.md
generated
vendored
@ -1,42 +0,0 @@
|
|||||||
crypt
|
|
||||||
=====
|
|
||||||
|
|
||||||
A golang implementation of crypt(3).
|
|
||||||
|
|
||||||
|
|
||||||
[](https://travis-ci.org/nyarla/go-crypt) [](https://godoc.org/github.com/nyarla/go-crypt)
|
|
||||||
|
|
||||||
EXAMPLES CODE
|
|
||||||
-------------
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/nyarlabo/go-crypt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Println(crypt.Crypt("testtest", "es")); // esDRYJnY4VaGM
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
WHY I FROKED IT?
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Original implementation is writte by iasija at 2009-12-08,
|
|
||||||
and original implementation is not supported golang 1.1 or later.
|
|
||||||
|
|
||||||
So I fork it for fix this issue, and I added documenation and test code.
|
|
||||||
|
|
||||||
Original implementation is hosting on [code.google.com/p/go-crypt](https://code.google.com/p/go-crypt),
|
|
||||||
and that source code is under the 3-Clause BSD.
|
|
||||||
|
|
||||||
NOTE: I could't find to iasija's contact address.
|
|
||||||
|
|
||||||
COPYRIGTS AND LICENSE
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
1. Original Implementation: Copyright (c) 2009 iasija All Rights Reserved. ([BSD-3-Clause](http://opensource.org/licenses/BSD-3-Clause))
|
|
||||||
2. Modification Codes: Copyright (c) 22013-2015 Naoki OKAMURA a.k.a nyarla <nyarla@thotep.net> Some Rights Reserved. ([BSD-3-Clause](http://opensource.org/licenses/BSD-3-Clause))
|
|
||||||
|
|
276
vendor/gitlab.com/nyarla/go-crypt/crypt.go
generated
vendored
276
vendor/gitlab.com/nyarla/go-crypt/crypt.go
generated
vendored
@ -1,276 +0,0 @@
|
|||||||
package crypt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var PC1_C = []byte{
|
|
||||||
57, 49, 41, 33, 25, 17, 9,
|
|
||||||
1, 58, 50, 42, 34, 26, 18,
|
|
||||||
10, 2, 59, 51, 43, 35, 27,
|
|
||||||
19, 11, 3, 60, 52, 44, 36,
|
|
||||||
}
|
|
||||||
|
|
||||||
var PC1_D = []byte{
|
|
||||||
63, 55, 47, 39, 31, 23, 15,
|
|
||||||
7, 62, 54, 46, 38, 30, 22,
|
|
||||||
14, 6, 61, 53, 45, 37, 29,
|
|
||||||
21, 13, 5, 28, 20, 12, 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
var PC2_C = []byte{
|
|
||||||
14, 17, 11, 24, 1, 5,
|
|
||||||
3, 28, 15, 6, 21, 10,
|
|
||||||
23, 19, 12, 4, 26, 8,
|
|
||||||
16, 7, 27, 20, 13, 2,
|
|
||||||
}
|
|
||||||
var PC2_D = []byte{
|
|
||||||
41, 52, 31, 37, 47, 55,
|
|
||||||
30, 40, 51, 45, 33, 48,
|
|
||||||
44, 49, 39, 56, 34, 53,
|
|
||||||
46, 42, 50, 36, 29, 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
var e2 = []byte{
|
|
||||||
32, 1, 2, 3, 4, 5,
|
|
||||||
4, 5, 6, 7, 8, 9,
|
|
||||||
8, 9, 10, 11, 12, 13,
|
|
||||||
12, 13, 14, 15, 16, 17,
|
|
||||||
16, 17, 18, 19, 20, 21,
|
|
||||||
20, 21, 22, 23, 24, 25,
|
|
||||||
24, 25, 26, 27, 28, 29,
|
|
||||||
28, 29, 30, 31, 32, 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
var IP = []byte{
|
|
||||||
58, 50, 42, 34, 26, 18, 10, 2,
|
|
||||||
60, 52, 44, 36, 28, 20, 12, 4,
|
|
||||||
62, 54, 46, 38, 30, 22, 14, 6,
|
|
||||||
64, 56, 48, 40, 32, 24, 16, 8,
|
|
||||||
57, 49, 41, 33, 25, 17, 9, 1,
|
|
||||||
59, 51, 43, 35, 27, 19, 11, 3,
|
|
||||||
61, 53, 45, 37, 29, 21, 13, 5,
|
|
||||||
63, 55, 47, 39, 31, 23, 15, 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
var FP = []byte{
|
|
||||||
40, 8, 48, 16, 56, 24, 64, 32,
|
|
||||||
39, 7, 47, 15, 55, 23, 63, 31,
|
|
||||||
38, 6, 46, 14, 54, 22, 62, 30,
|
|
||||||
37, 5, 45, 13, 53, 21, 61, 29,
|
|
||||||
36, 4, 44, 12, 52, 20, 60, 28,
|
|
||||||
35, 3, 43, 11, 51, 19, 59, 27,
|
|
||||||
34, 2, 42, 10, 50, 18, 58, 26,
|
|
||||||
33, 1, 41, 9, 49, 17, 57, 25,
|
|
||||||
}
|
|
||||||
|
|
||||||
var S = [][]byte{
|
|
||||||
[]byte{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
|
|
||||||
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
|
|
||||||
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
|
|
||||||
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13},
|
|
||||||
[]byte{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
|
|
||||||
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
|
|
||||||
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
|
|
||||||
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9},
|
|
||||||
[]byte{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
|
|
||||||
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
|
|
||||||
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
|
|
||||||
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12},
|
|
||||||
[]byte{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
|
|
||||||
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
|
|
||||||
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
|
|
||||||
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14},
|
|
||||||
[]byte{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
|
|
||||||
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
|
|
||||||
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
|
|
||||||
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3},
|
|
||||||
[]byte{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
|
|
||||||
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
|
|
||||||
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
|
|
||||||
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13},
|
|
||||||
[]byte{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
|
|
||||||
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
|
|
||||||
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
|
|
||||||
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12},
|
|
||||||
[]byte{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
|
|
||||||
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
|
|
||||||
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
|
|
||||||
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11},
|
|
||||||
}
|
|
||||||
|
|
||||||
var P = []byte{
|
|
||||||
16, 7, 20, 21, 29, 12, 28, 17,
|
|
||||||
1, 15, 23, 26, 5, 18, 31, 10,
|
|
||||||
2, 8, 24, 14, 32, 27, 3, 9,
|
|
||||||
19, 13, 30, 6, 22, 11, 4, 25,
|
|
||||||
}
|
|
||||||
|
|
||||||
var shift = []int{1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}
|
|
||||||
|
|
||||||
// Crypt is a implementation of crypt(3) like as perl or ruby and ...etc.
|
|
||||||
//
|
|
||||||
// Behavior of this func is same as perl-5.18.2's crypt on OSX Yosemite.
|
|
||||||
func Crypt(pw string, salt string) string {
|
|
||||||
if sLen := len(salt); sLen < 2 {
|
|
||||||
src := []byte(salt)
|
|
||||||
for len(src) < 2 {
|
|
||||||
src = append(src, 0x00)
|
|
||||||
}
|
|
||||||
salt = string(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
block := make([]byte, 66)
|
|
||||||
|
|
||||||
for i := 0; i < 8 && i < len(pw); i++ {
|
|
||||||
for j := 0; j < 7; j++ {
|
|
||||||
block[(8*i)+j] = (pw[i] >> byte(6-j)) & 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
C := make([]byte, 28)
|
|
||||||
D := make([]byte, 28)
|
|
||||||
|
|
||||||
for i := 0; i < 28; i++ {
|
|
||||||
C[i] = block[PC1_C[i]-1]
|
|
||||||
D[i] = block[PC1_D[i]-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
KS := make([][]byte, 16)
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
KS[i] = make([]byte, 48)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
for k := 0; k < shift[i]; k++ {
|
|
||||||
t := C[0]
|
|
||||||
for j := 0; j < 28-1; j++ {
|
|
||||||
C[j] = C[j+1]
|
|
||||||
}
|
|
||||||
C[27] = t
|
|
||||||
t = D[0]
|
|
||||||
for j := 0; j < 28-1; j++ {
|
|
||||||
D[j] = D[j+1]
|
|
||||||
}
|
|
||||||
D[27] = t
|
|
||||||
}
|
|
||||||
for j := 0; j < 24; j++ {
|
|
||||||
KS[i][j] = C[PC2_C[j]-1]
|
|
||||||
KS[i][j+24] = D[PC2_D[j]-28-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
E := make([]byte, 48)
|
|
||||||
for i := 0; i < 48; i++ {
|
|
||||||
E[i] = e2[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
iobuf := make([]byte, 16)
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
c := byte(salt[i])
|
|
||||||
iobuf[i] = c
|
|
||||||
|
|
||||||
if c > 'Z' {
|
|
||||||
c -= 6
|
|
||||||
}
|
|
||||||
if c > '9' {
|
|
||||||
c -= 7
|
|
||||||
}
|
|
||||||
|
|
||||||
c -= '.'
|
|
||||||
|
|
||||||
for j := 0; j < 6; j++ {
|
|
||||||
if (c>>byte(j))&1 != 0 {
|
|
||||||
k := E[6*i+j]
|
|
||||||
E[6*i+j] = E[6*i+j+24]
|
|
||||||
E[6*i+j+24] = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 66; i++ {
|
|
||||||
block[i] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
R := make([]byte, 32)
|
|
||||||
L := make([]byte, 32)
|
|
||||||
DMY := make([]byte, 32)
|
|
||||||
preS := make([]byte, 48)
|
|
||||||
f := make([]byte, 32)
|
|
||||||
dmy_block := make([]byte, 64)
|
|
||||||
|
|
||||||
for m := 0; m < 25; m++ {
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
L[i] = block[IP[i]-1]
|
|
||||||
}
|
|
||||||
for i := 32; i < 64; i++ {
|
|
||||||
R[i-32] = block[IP[i]-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
for j := 0; j < 32; j++ {
|
|
||||||
DMY[j] = R[j]
|
|
||||||
}
|
|
||||||
for j := 0; j < 48; j++ {
|
|
||||||
preS[j] = R[E[j]-1] ^ KS[i][j]
|
|
||||||
}
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
t := 6 * j
|
|
||||||
k := S[j][(preS[t+0]<<5)+
|
|
||||||
(preS[t+1]<<3)+
|
|
||||||
(preS[t+2]<<2)+
|
|
||||||
(preS[t+3]<<1)+
|
|
||||||
(preS[t+4]<<0)+
|
|
||||||
(preS[t+5]<<4)]
|
|
||||||
|
|
||||||
t = 4 * j
|
|
||||||
f[t+0] = (k >> 3) & 01
|
|
||||||
f[t+1] = (k >> 2) & 01
|
|
||||||
f[t+2] = (k >> 1) & 01
|
|
||||||
f[t+3] = (k >> 0) & 01
|
|
||||||
}
|
|
||||||
for j := 0; j < 32; j++ {
|
|
||||||
R[j] = L[j] ^ f[P[j]-1]
|
|
||||||
}
|
|
||||||
for j := 0; j < 32; j++ {
|
|
||||||
L[j] = DMY[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
L[i], R[i] = R[i], L[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
dmy_block[i] = L[i]
|
|
||||||
}
|
|
||||||
for i := 32; i < 64; i++ {
|
|
||||||
dmy_block[i] = R[i-32]
|
|
||||||
}
|
|
||||||
for i := 0; i < 64; i++ {
|
|
||||||
block[i] = dmy_block[FP[i]-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var i int
|
|
||||||
for i = 0; i < 11; i++ {
|
|
||||||
c := byte(0)
|
|
||||||
for j := 0; j < 6; j++ {
|
|
||||||
c = c << 1
|
|
||||||
c = c | block[6*i+j]
|
|
||||||
}
|
|
||||||
c = c + '.'
|
|
||||||
|
|
||||||
if c > '9' {
|
|
||||||
c += 7
|
|
||||||
}
|
|
||||||
if c > 'Z' {
|
|
||||||
c += 6
|
|
||||||
}
|
|
||||||
|
|
||||||
iobuf[i+2] = c
|
|
||||||
}
|
|
||||||
|
|
||||||
iobuf[i+2] = 0
|
|
||||||
|
|
||||||
return string(bytes.Replace(iobuf, []byte{0x00}, []byte{}, -1))
|
|
||||||
}
|
|
18
vendor/gitlab.com/nyarla/go-crypt/crypt_test.go
generated
vendored
18
vendor/gitlab.com/nyarla/go-crypt/crypt_test.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
package crypt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCrypt(t *testing.T) {
|
|
||||||
if ret := Crypt("testtest", "es"); ret != `esDRYJnY4VaGM` {
|
|
||||||
t.Fatal(fmt.Sprintf(`result of Crypt is musmatch: %+v`, []byte(ret)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleCrypt() {
|
|
||||||
fmt.Println(Crypt("testtest", "es"))
|
|
||||||
// Output:
|
|
||||||
// esDRYJnY4VaGM
|
|
||||||
}
|
|
11
vendor/gitlab.com/nyarla/go-crypt/doc.go
generated
vendored
11
vendor/gitlab.com/nyarla/go-crypt/doc.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// Package crypt is a implementation of crypt(3) by golang.
|
|
||||||
//
|
|
||||||
// This is a fork of iasija's orignal implementation.
|
|
||||||
//
|
|
||||||
// Orignal soruce code Copyrights (C) iasija All rights reserved,
|
|
||||||
// and original source code is under the 3-Clause BSD.
|
|
||||||
//
|
|
||||||
// Modification codes for supporting latest golang and added test codes are
|
|
||||||
// Copyright (c) 2013-2014 Naoki OKAMURA <nyarla@thotep.net>,
|
|
||||||
// and modifiration code and test codes are under same as the orignal source code license. (3-Clause BSD)
|
|
||||||
package crypt
|
|
@ -4,6 +4,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const VERSION = 0.3
|
||||||
|
|
||||||
var ApiVersionRouting = map[string]struct {
|
var ApiVersionRouting = map[string]struct {
|
||||||
AuthFunction
|
AuthFunction
|
||||||
DispatchFunction
|
DispatchFunction
|
||||||
@ -12,7 +14,7 @@ var ApiVersionRouting = map[string]struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showVersion(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
func showVersion(u *User, args []string, body io.ReadCloser) (interface{}, error) {
|
||||||
m := map[string]interface{}{"version": 0.2}
|
m := map[string]interface{}{"version": VERSION}
|
||||||
|
|
||||||
if u != nil {
|
if u != nil {
|
||||||
m["youare"] = *u
|
m["youare"] = *u
|
||||||
|
Loading…
x
Reference in New Issue
Block a user