code.gitea.io/gitea@v1.19.3/modules/avatar/avatar.go (about)

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