Работа с изображениями 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. Вы можете сделать еще один шаг, используя цвет фона, определяемый инициалами. Один из способов сделать это - создать фрагмент или массив шестнадцатеричных цветовых строк и выбрать цвет из фрагмента на основе хэша инициалов.