Работа с изображениями Go
Веб-приложениям часто требуется отображать аватар для пользователей, а пользователи не всегда стремятся загружать свои изображения. Популярный вариант - создавать аватары для ваших пользователей на основе инициалов их имен. В этом руководстве мы рассмотрим, как создавать такие аватары в Go и обслуживать их через HTTP с помощью маршрутизатора chi.
Настройка
Чтобы продолжить, вам потребуется достаточно свежая версия Go (версия 1.14 или выше).
Примечание. Команды оболочки, используемые ниже, предназначены для Linux / macOS, не стесняйтесь использовать эквивалент вашей операционной системы, если он отличается.
Сначала создайте папку для проекта и инициализируйте новый модуль Go:
$ mkdir go-avatars && cd go-avatars
$ go mod init gitlab.com/idoko/go-avatars
Затем добавьте необходимые зависимости, выполнив следующую команду в своем рабочем каталоге (т.е. go-avatars ):
$ go get golang.org/x/image github.com/golang/freetype github.com/go-chi/chi
Зависимости включают в себя image, который предоставляет методы для работы с изображениями в Go, freetype для работы со шрифтами (и записи на изображениях), а также маршрутизатор chi для обслуживания сгенерированных аватаров через HTTP.
Создайте холст изображения
Чтобы подготовить фон для наших аватарок, мы создаем файл main.go в рабочем каталоге и реализуем две функции - main и createAvatar. Откройте файл main.go и добавьте блок кода ниже:
package main
import (
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"log"
"os"
"time"
)
func main() {
initials := "LR"
size := 200
avatar, err := createAvatar(size, initials)
if err != nil {
log.Fatal(err)
}
filename := fmt.Sprintf("out-%d.png", time.Now().Unix())
file, err := os.Create(filename)
if err != nil {
log.Fatal(err)
}
png.Encode(file, avatar)
}
func createAvatar(size int, initials string) (*image.RGBA, error) {
width, height := size, size
bgColor, err := hexToRGBA("#764abc")
if err != nil {
log.Fatal(err)
}
background := image.NewRGBA(image.Rect(0, 0, width, height))
draw.Draw(background, background.Bounds(), &image.Uniform{C: bgColor},
image.Point{}, draw.Src)
//drawText(background, initials)
return background, err
}
Функция createAvatar создает квадратное полотно, ширину и высоту такие же, как параметр size, передаваемый в функцию. Он преобразует цветовой код HEX в его эквивалент RGBA, а затем RGBA равномерно окрашивается по холсту. Далее мы реализуем функцию преобразования hexToRGBA между цветами.
Преобразование HEX-кода в цвета RGBA
Добавьте реализацию функции hextorgb к файлу main.go:
func hexToRGBA(hex string) (color.RGBA, error) {
var (
rgba color.RGBA
err error
errInvalidFormat = fmt.Errorf("invalid")
)
rgba.A = 0xff
if hex[0] != '#' {
return rgba, errInvalidFormat
}
hexToByte := func(b byte) byte {
switch {
case b >= '0' && b <= '9':
return b - '0'
case b >= 'a' && b <= 'f':
return b - 'a' + 10
case b >= 'A' && b <= 'F':
return b - 'A' + 10
}
err = errInvalidFormat
return 0
}
switch len(hex) {
case 7:
rgba.R = hexToByte(hex[1])<<4 + hexToByte(hex[2])
rgba.G = hexToByte(hex[3])<<4 + hexToByte(hex[4])
rgba.B = hexToByte(hex[5])<<4 + hexToByte(hex[6])
case 4:
rgba.R = hexToByte(hex[1]) * 17
rgba.G = hexToByte(hex[2]) * 17
rgba.B = hexToByte(hex[3]) * 17
default:
err = errInvalidFormat
}
return rgba, err
}
Реализация использует код из библиотеки gox и работает путем преобразования цветового кода HEX в байтовый ряд. Для шестнадцатеричных триплетов (т. е. шестизначных, трехбайтовых шестнадцатеричных чисел) первая цифра каждого байта умножается на 16 (сдвиг влево на 4 бита) и добавляется ко второй цифре того же байта, чтобы получить эквивалент RGB.
Если шестнадцатеричная строка состоит из трех цифр (скажем, #FFF), то нам нужно только умножить каждую из них на 17, чтобы получить эквивалент RGB.
Вы можете узнать больше о преобразованиях веб-цветов на этой странице в Википедии.
Запустите файл main.go с помощью go run ./main.go, чтобы сгенерировать однородно окрашенный файл, подобный приведенному ниже (названный out-xxxxxxxx.png) в каталоге проекта.
Нарисовать текст на фоне
Далее мы реализуем функцию drawText, отвечающую за запись инициалов на пустом холсте. Откройте файл main.go в своем редакторе и добавьте в него приведенный ниже код:
func drawText(canvas *image.RGBA, text string) error {
var (
fgColor image.Image
fontFace *truetype.Font
err error
fontSize = 128.0
)
fgColor = image.White
fontFace, err = freetype.ParseFont(goregular.TTF)
fontDrawer := &font.Drawer{
Dst: canvas,
Src: fgColor,
Face: truetype.NewFace(fontFace, &truetype.Options{
Size: fontSize,
Hinting: font.HintingFull,
}),
}
textBounds, _ := fontDrawer.BoundString(text)
xPosition := (fixed.I(canvas.Rect.Max.X) - fontDrawer.MeasureString(text)) / 2
textHeight := textBounds.Max.Y - textBounds.Min.Y
yPosition := fixed.I((canvas.Rect.Max.Y)-textHeight.Ceil())/2 + fixed.I(textHeight.Ceil())
fontDrawer.Dot = fixed.Point26_6{
X: xPosition,
Y: yPosition,
}
fontDrawer.DrawString(text)
return err
}
Функция принимает указатель image.RGBA в качестве параметра, что гарантирует, что она изменяет то же изображение, переданное ей. Для рендеринга он использует шрифт goregular из стандартной библиотеки Go.
Рисование самого текста использует структуру font.Drawer, основная задача которой - писать на изображениях. Мы передаем холст аватара в качестве пункта назначения ящика (Dst) и полностью белое изображение в качестве исходного изображения (Src), которое отображает текст белым цветом.
Мы также вычисляем горизонтальную начальную точку (xPosition) для нашего текста, сначала измеряя длину текста, вычитая длину из общей ширины холста и деля результат на 2 (чтобы учесть левое и правое поля).
Аналогично, вертикальное положение (yPosition) вычисляется, сначала уменьшая вдвое разницу между высотой холста и высотой текста и снова добавляя высоту текста, чтобы текст встал на место.
Не забудьте раскомментировать строку drawText(background, initials)в функции createAvatar.
Кроме того, вам может потребоваться импортировать зависимости вручную, если ваш редактор не сделал этого за вас.
Запустите файл main.go с помощью go run ./main.go, и вы должны увидеть сгенерированное изображение в своем рабочем каталоге.
Рендеринг сгенерированных изображений с помощью маршрутизатора chi
Чтобы наши аватары были доступны в браузере, мы изменим нашу функцию main и заставим ее отображать сгенерированные изображения через HTTP. Замените существующую функцию main приведенным ниже кодом:
router := chi.NewRouter()
router.Use(middleware.Logger)
router.Get("/avatar", func(w http.ResponseWriter, r *http.Request) {
initials := r.FormValue("initials")
size, err := strconv.Atoi(r.FormValue("size"))
if err != nil {
size = 200
}
avatar, err := createAvatar(size, initials)
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "image/png")
png.Encode(w, avatar)
})
http.ListenAndServe(":3000", router)
Приведенный выше фрагмент кода сначала настраивает маршрут /avatar с помощью chi для обработки HTTP-запросов. Затем функция извлекает инициалы и размер из параметров запроса и использует их для создания аватаров.
Поскольку png.Encode() принимает интерфейс io.Writer (который реализован http.ResponseWriter), наша новая функция main может напрямую обслуживать сгенерированное изображение для отображения браузером.
Запустите HTTP-сервер, запустив go run ./main.go в своем терминале, и посетите http: // localhost: 3000 / avatar? Initials = JD & size = 300 в своем терминале, чтобы увидеть результат.
Вывод
Я считаю, что динамические аватары более эстетичны, чем статические аватары по умолчанию, и в этой статье мы увидели способ реализовать их на Golang. Вы можете сделать еще один шаг, используя цвет фона, определяемый инициалами. Один из способов сделать это - создать фрагмент или массив шестнадцатеричных цветовых строк и выбрать цвет из фрагмента на основе хэша инициалов.