2014-03-23 14:13:23 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-03-23 08:24:09 +04:00
package avatar
import (
2019-05-25 14:46:14 +03:00
"bytes"
2014-03-23 08:24:09 +04:00
"fmt"
2014-03-23 11:55:27 +04:00
"image"
2015-08-09 06:46:10 +03:00
"image/color/palette"
2019-05-25 14:46:14 +03:00
// Enable PNG support:
_ "image/png"
2015-08-09 06:46:10 +03:00
"math/rand"
2014-03-23 08:24:09 +04:00
"time"
2014-03-23 11:55:27 +04:00
2019-05-25 14:46:14 +03:00
"code.gitea.io/gitea/modules/setting"
2015-09-27 00:54:02 +03:00
"github.com/issue9/identicon"
2019-05-25 14:46:14 +03:00
"github.com/nfnt/resize"
"github.com/oliamb/cutter"
2014-03-23 08:24:09 +04:00
)
2016-11-25 11:37:04 +03:00
// AvatarSize returns avatar's size
const AvatarSize = 290
2015-08-09 06:46:10 +03:00
2016-11-25 11:37:04 +03:00
// RandomImageSize generates and returns a random avatar image unique to input data
2016-02-21 01:10:05 +03:00
// in custom size (height and width).
2016-02-15 07:14:55 +03:00
func RandomImageSize ( size int , data [ ] byte ) ( image . Image , error ) {
2015-08-09 06:46:10 +03:00
randExtent := len ( palette . WebSafe ) - 32
rand . Seed ( time . Now ( ) . UnixNano ( ) )
colorIndex := rand . Intn ( randExtent )
backColorIndex := colorIndex - 1
if backColorIndex < 0 {
backColorIndex = randExtent - 1
}
2016-02-15 07:14:55 +03:00
// Define size, background, and forecolor
imgMaker , err := identicon . New ( size ,
2015-08-09 06:46:10 +03:00
palette . WebSafe [ backColorIndex ] , palette . WebSafe [ colorIndex : colorIndex + 32 ] ... )
if err != nil {
2016-02-15 07:14:55 +03:00
return nil , fmt . Errorf ( "identicon.New: %v" , err )
2015-08-09 06:46:10 +03:00
}
return imgMaker . Make ( data ) , nil
}
2016-02-21 01:10:05 +03:00
// RandomImage generates and returns a random avatar image unique to input data
// in default size (height and width).
2016-02-15 07:14:55 +03:00
func RandomImage ( data [ ] byte ) ( image . Image , error ) {
2016-11-25 11:37:04 +03:00
return RandomImageSize ( AvatarSize , data )
2014-03-23 08:24:09 +04:00
}
2019-05-25 14:46:14 +03:00
// Prepare accepts a byte slice as input, validates it contains an image of an
// acceptable format, and crops and resizes it appropriately.
func Prepare ( data [ ] byte ) ( * image . Image , error ) {
imgCfg , _ , err := image . DecodeConfig ( bytes . NewReader ( data ) )
if err != nil {
return nil , fmt . Errorf ( "DecodeConfig: %v" , err )
}
if imgCfg . Width > setting . AvatarMaxWidth {
return nil , fmt . Errorf ( "Image width is too large: %d > %d" , imgCfg . Width , setting . AvatarMaxWidth )
}
if imgCfg . Height > setting . AvatarMaxHeight {
return nil , fmt . Errorf ( "Image height is too large: %d > %d" , imgCfg . Height , setting . AvatarMaxHeight )
}
img , _ , err := image . Decode ( bytes . NewReader ( data ) )
if err != nil {
return nil , fmt . Errorf ( "Decode: %v" , err )
}
if imgCfg . Width != imgCfg . Height {
var newSize , ax , ay int
if imgCfg . Width > imgCfg . Height {
newSize = imgCfg . Height
ax = ( imgCfg . Width - imgCfg . Height ) / 2
} else {
newSize = imgCfg . Width
ay = ( imgCfg . Height - imgCfg . Width ) / 2
}
img , err = cutter . Crop ( img , cutter . Config {
Width : newSize ,
Height : newSize ,
Anchor : image . Point { ax , ay } ,
} )
if err != nil {
return nil , err
}
}
img = resize . Resize ( AvatarSize , AvatarSize , img , resize . NearestNeighbor )
return & img , nil
}