github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/avatar/avatar.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 // Copyright 2014 The Gogs Authors. All rights reserved. 7 // Use of this source code is governed by a MIT-style 8 // license that can be found in the LICENSE file. 9 10 package avatar 11 12 import ( 13 "bytes" 14 "fmt" 15 "image" 16 "image/color" 17 18 _ "image/gif" // for processing gif images 19 _ "image/jpeg" // for processing jpeg images 20 _ "image/png" // for processing png images 21 22 "github.com/gitbundle/modules/avatar/identicon" 23 "github.com/gitbundle/modules/setting" 24 25 "github.com/nfnt/resize" 26 "github.com/oliamb/cutter" 27 ) 28 29 // AvatarSize returns avatar's size 30 const AvatarSize = 290 31 32 // RandomImageSize generates and returns a random avatar image unique to input data 33 // in custom size (height and width). 34 func RandomImageSize(size int, data []byte) (image.Image, error) { 35 // we use white as background, and use dark colors to draw blocks 36 imgMaker, err := identicon.New(size, color.White, identicon.DarkColors...) 37 if err != nil { 38 return nil, fmt.Errorf("identicon.New: %v", err) 39 } 40 return imgMaker.Make(data), nil 41 } 42 43 // RandomImage generates and returns a random avatar image unique to input data 44 // in default size (height and width). 45 func RandomImage(data []byte) (image.Image, error) { 46 return RandomImageSize(AvatarSize, data) 47 } 48 49 // Prepare accepts a byte slice as input, validates it contains an image of an 50 // acceptable format, and crops and resizes it appropriately. 51 func Prepare(data []byte) (*image.Image, error) { 52 imgCfg, _, err := image.DecodeConfig(bytes.NewReader(data)) 53 if err != nil { 54 return nil, fmt.Errorf("DecodeConfig: %v", err) 55 } 56 if imgCfg.Width > setting.Avatar.MaxWidth { 57 return nil, fmt.Errorf("Image width is too large: %d > %d", imgCfg.Width, setting.Avatar.MaxWidth) 58 } 59 if imgCfg.Height > setting.Avatar.MaxHeight { 60 return nil, fmt.Errorf("Image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight) 61 } 62 63 img, _, err := image.Decode(bytes.NewReader(data)) 64 if err != nil { 65 return nil, fmt.Errorf("Decode: %v", err) 66 } 67 68 if imgCfg.Width != imgCfg.Height { 69 var newSize, ax, ay int 70 if imgCfg.Width > imgCfg.Height { 71 newSize = imgCfg.Height 72 ax = (imgCfg.Width - imgCfg.Height) / 2 73 } else { 74 newSize = imgCfg.Width 75 ay = (imgCfg.Height - imgCfg.Width) / 2 76 } 77 78 img, err = cutter.Crop(img, cutter.Config{ 79 Width: newSize, 80 Height: newSize, 81 Anchor: image.Point{ax, ay}, 82 }) 83 if err != nil { 84 return nil, err 85 } 86 } 87 88 img = resize.Resize(AvatarSize, AvatarSize, img, resize.Bilinear) 89 return &img, nil 90 }