2014-03-23 18:13:23 +08: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 12:24:09 +08:00
package avatar
import (
2019-05-25 13:46:14 +02:00
"bytes"
2014-03-23 12:24:09 +08:00
"fmt"
2014-03-23 15:55:27 +08:00
"image"
2015-08-09 11:46:10 +08:00
"image/color/palette"
2020-10-14 21:07:51 +08:00
2021-06-05 14:32:19 +02:00
_ "image/gif" // for processing gif images
_ "image/jpeg" // for processing jpeg images
_ "image/png" // for processing png images
2014-03-23 15:55:27 +08:00
2019-05-25 13:46:14 +02:00
"code.gitea.io/gitea/modules/setting"
2021-05-10 08:45:17 +02:00
"code.gitea.io/gitea/modules/util"
2019-05-25 13:46:14 +02:00
2015-09-26 17:54:02 -04:00
"github.com/issue9/identicon"
2019-05-25 13:46:14 +02:00
"github.com/nfnt/resize"
"github.com/oliamb/cutter"
2014-03-23 12:24:09 +08:00
)
2016-11-25 16:37:04 +08:00
// AvatarSize returns avatar's size
const AvatarSize = 290
2015-08-09 11:46:10 +08:00
2016-11-25 16:37:04 +08:00
// RandomImageSize generates and returns a random avatar image unique to input data
2016-02-20 17:10:05 -05:00
// in custom size (height and width).
2016-02-14 23:14:55 -05:00
func RandomImageSize ( size int , data [ ] byte ) ( image . Image , error ) {
2015-08-09 11:46:10 +08:00
randExtent := len ( palette . WebSafe ) - 32
2021-05-10 08:45:17 +02:00
integer , err := util . RandomInt ( int64 ( randExtent ) )
if err != nil {
return nil , fmt . Errorf ( "util.RandomInt: %v" , err )
}
colorIndex := int ( integer )
2015-08-09 11:46:10 +08:00
backColorIndex := colorIndex - 1
if backColorIndex < 0 {
backColorIndex = randExtent - 1
}
2016-02-14 23:14:55 -05:00
// Define size, background, and forecolor
imgMaker , err := identicon . New ( size ,
2015-08-09 11:46:10 +08:00
palette . WebSafe [ backColorIndex ] , palette . WebSafe [ colorIndex : colorIndex + 32 ] ... )
if err != nil {
2016-02-14 23:14:55 -05:00
return nil , fmt . Errorf ( "identicon.New: %v" , err )
2015-08-09 11:46:10 +08:00
}
return imgMaker . Make ( data ) , nil
}
2016-02-20 17:10:05 -05:00
// RandomImage generates and returns a random avatar image unique to input data
// in default size (height and width).
2016-02-14 23:14:55 -05:00
func RandomImage ( data [ ] byte ) ( image . Image , error ) {
2016-11-25 16:37:04 +08:00
return RandomImageSize ( AvatarSize , data )
2014-03-23 12:24:09 +08:00
}
2019-05-25 13:46:14 +02: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 )
}
2020-10-14 21:07:51 +08:00
if imgCfg . Width > setting . Avatar . MaxWidth {
return nil , fmt . Errorf ( "Image width is too large: %d > %d" , imgCfg . Width , setting . Avatar . MaxWidth )
2019-05-25 13:46:14 +02:00
}
2020-10-14 21:07:51 +08:00
if imgCfg . Height > setting . Avatar . MaxHeight {
return nil , fmt . Errorf ( "Image height is too large: %d > %d" , imgCfg . Height , setting . Avatar . MaxHeight )
2019-05-25 13:46:14 +02:00
}
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
}
}
2020-09-06 21:53:33 +02:00
img = resize . Resize ( AvatarSize , AvatarSize , img , resize . Bilinear )
2019-05-25 13:46:14 +02:00
return & img , nil
}