[Разработка веб-сайтов, Работа с иконками, Обработка изображений, Go] Создание изображений в runtime (favicon, watermark, нарезка картинок) #golang
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В Go есть возможность создавать файлы изображений. С помощью этого мы можем создавать картинки на лету (в runtime).Где же это может пригодится?Вот небольшой список того, что мы можем создать используя данный функционал:
- Favicon
- Pixel-трекер
- Placeholder
- Наложение текста (watermark) на изображение
- Нарезка изображений
1. FaviconЧасто Go приложения рассматриваются как серверная часть для отдачи контента для внутренних и/или внешних сервисов и может отдавать контент для Вашего сайта. Желательно, чтобы у Вас был внешний сервис (слой) для отдачи ответа - некий getaway, чтобы отдавать только нужный payload и, в том числе, без веб-специфичных запросов.Под веб-специфичными запросами от клиента к серверной части - я подразумеваю запросы favicon и различные манифесты, которые принято рассматривать в большей степени как "фронтовые" типы ресурсов. То есть за их работу в большинстве случаев отвечают те же специалисты, которые и делают "фронтовую" часть проекта.Но, что если ваше Go приложение также сможет отдавать favicon и манифесты самостоятельно. И не просто брать статичные файлы favicon и манифеста, а генерить/создавать эти массивы байт в runtime. Это позволит нашему приложению не зависеть от этих статичных файлов.Вы, конечно, можете отдавать статичный файл:
func faviconHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "relative/path/to/favicon.ico")
}
...
func main() {
http.HandleFunc("/favicon.ico", faviconHandler)
}
Но мы же хотим создавать favicon динамически.Для динамического создания favicon есть, как минимум, два похожих способа:
- Первый способ
import (
"bytes"
"image"
"image/gif"
"net/http"
...
)
var buf bytes.Buffer
gif.Encode(&buf, image.Rect(0, 0, 16, 16), nil)
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Length", strconv.Itoa(len(buf.Bytes())))
w.Write(buf.Bytes())
Второй способ:
import (
"bytes"
"image"
"image/color"
"image/draw"
"image/jpeg"
...
)
buf := new(bytes.Buffer)
m := image.NewRGBA(image.Rect(0, 0, 16, 16))
clr := color.RGBA{B: 0, A: 0}
draw.Draw(m, m.Bounds(), image.NewUniform(clr), image.Pointer{}, draw.Src)
jpeg.Encode(buffer, img, nil)
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))
w.Write(buf.Bytes())
2. Pixel-трекерPixel-трекер широко используется при арбитраже трафика.
var buf bytes.Buffer
gif.Encode(&buf, image.Rect(0, 0, 1, 1, nil))
Динамическая генерация такого трекера позволит Вам в одном приложении отдавать и pixel, и собирать данные.
Для генерации placeholder-ов мы будем использовать тот же подход, что и для картинок с фиксированным размером, но с возможностью добавлять текст и изменять размеры картинки и шрифта.Во-первых, определимся с требованиями:
- на вход к нам приходим запрос вида: /ШИРИНА/ВЫСОТА/ЦВЕТ/ТЕКСТ/ЦВЕТ_ТЕКСТА/РАЗМЕР_ШРИФТАНапример: /600/200/622E68/Placeholder (заглушка)/FFFFFF
- текст должен располагаться по середине картинки
- текст приходит в запросе от клиента или собирается по формату: "ШИРИНА х ВЫСОТА"
- текст должен поддерживать возможность латиницы и кириллицы
- предусмотреть параметры по умолчанию
- на выходе мы отдаём байты картинки
Разбор параметров начнём с пакета по работе с цветами картинки и текста (package colors).Цвет будет приходить как строка в HEX формате. Другие форматы цвета, как входного параметра - не предвидятся.Пакет для работы с цветами:
package colors
import (
"image/color"
"strconv"
)
type rgb struct {
red uint8
green uint8
blue uint8
}
func ToRGBA(h string) (color.RGBA, error) {
rgb, err := hex2RGB(h)
if err != nil {
return color.RGBA{}, err
}
return color.RGBA{R: rgb.red, G: rgb.green, B: rgb.blue, A: 255}, nil
}
func hex2RGB(hex string) (rgb, error) {
values, err := strconv.ParseUint(hex, 16, 32)
if err != nil {
return rgb{}, err
}
return rgb{
red: uint8(values >> 16),
green: uint8((values >> 8) & 0xFF),
blue: uint8(values & 0xFF),
}, nil
}
Непосредственно пакет сборки нашей картинки:
pacakge img
const (
// Параметры по умолчанию
imgColorDefault = "E5E5E5"
msgColorDefault = "AAAAAA"
imgWDefault = 300
imgHDefault = 300
fontSizeDefault = 0
dpiDefault float64 = 72
fontfileDefault = "wqy-zenhei.ttf"
)
// Do - входная точка.
func Do(params []string) (*bytes.Buffer, error) {
// fetch img params: imgW, imgH, text, etc
// Соберём структуру Текста
label := Label{Text: msg, FontSize: fontSize, Color: msgColor}
// Соберём структуру Картинки с нужными полями - высота, ширина, цвет и текст
img := Img{Width: imgW, Height: imgH, Color: imgColor, Label: label}
// Сгенерим нашу картинку с текстом
return img.generate()
}
// generate - соберёт картинку по нужным размерам, цветом и текстом.
func (i Img) generate() (*bytes.Buffer, error) {
// Если есть размеры и нет требований по Тексту - соберём Текст по умолчанию.
if ((i.Width > 0 || i.Height > 0) && i.Text == "") || i.Text == "" {
i.Text = fmt.Sprintf("%d x %d", i.Width, i.Height)
}
// Если нет требований по размеру шрифта - подберём его исходя из размеров картинки.
if i.FontSize == 0 {
i.FontSize = i.Width / 10
if i.Height < i.Width {
i.FontSize = i.Height / 5
}
}
// Переведём цвет из строки в color.RGBA.
clr, err := colors.ToRGBA(i.Color)
if err != nil {
return nil, err
}
// Создадим in-memory картинку с нужными размерами.
m := image.NewRGBA(image.Rect(0, 0, i.Width, i.Height))
// Отрисуем картинку:
// - по размерам (Bounds)
// - и с цветом (Uniform - обёртка над color.Color c Image функциями)
// - исходя из точки (Point), как базовой картинки
// - заполним цветом нашу Uniform (draw.Src)
draw.Draw(m, m.Bounds(), image.NewUniform(clr), image.Point{}, draw.Src)
// Добавим текст в картинку.
if err = i.drawLabel(m); err != nil {
return nil, err
}
var im image.Image = m
// Выделим память под нашу данные (байты картинки).
buffer := &bytes.Buffer{}
// Закодируем картинку в нашу аллоцированную память.
err = jpeg.Encode(buffer, im, nil)
return buffer, err
}
// drawLabel - добавит текст на картинку.
func (i *Img) drawLabel(m *image.RGBA) error {
// Разберём цвет текста из строки в RGBA.
clr, err := colors.ToRGBA(i.Label.Color)
if err != nil {
return err
}
// Получим шрифт (должен работать и с латиницей и с кириллицей).
fontBytes, err := ioutil.ReadFile(fontfileDefault)
if err != nil {
return err
}
fnt, err := truetype.Parse(fontBytes)
if err != nil {
return err
}
// Подготовим Drawer для отрисовки текста на картинке.
d := &font.Drawer{
Dst: m,
Src: image.NewUniform(clr),
Face: truetype.NewFace(fnt, &truetype.Options{
Size: float64(i.FontSize),
DPI: dpiDefault,
Hinting: font.HintingNone,
}),
}
// Зададим базовую линию.
d.Dot = fixed.Point26_6{
X: (fixed.I(i.Width) - d.MeasureString(i.Text)) / 2,
Y: fixed.I((i.Height+i.FontSize)/2 - 12),
}
// Непосредственно отрисовка текста в нашу RGBA картинку.
d.DrawString(i.Text)
return nil
}
Пример, на запрос:
http://localhost:8080/600/200/004620/Заглушка/FFFFFF/50
получим:
Обратите внимание: мы можем создавать изображение с текстом на латинице и/или на кириллице.А на такой запрос:
http://localhost:8080/480/200
4. Наложение текста (watermark) на изображениеWatermark может пригодится в таких кейсах:
- для указания правообладателя на публикуемом изображении
- на изображение можно накладывать текст рекламного характера
- назначение изображения и его ограничения для использования
Например, указать, что данное изображение или фото могут быть использованы только для определённых целей и/или в ограниченные сроки.5. Нарезка изображенийНе редко на платформе есть необходимость хранить набор изображений и их нарезки, по размерам и форматам.В в какой-то момент может прийти требование изменить набор нарезанных размеров.В этом случае необходимо производить нарезку новых размеров и, возможно, удалить другие размеры.Возможны два сценария реализации нарезки:
- разовая (нарезка для всего набора по требованию)
- динамическая (нарезка в runtime)
У разовой нарезки есть неоспоримое преимущество - мы разово (например, раз в сутки) нарезаем все картинки или часть из них.То есть, в данном случае мы требовательны к дисковому пространству. Быстрые диски ближе к клиенту и ближе к горячим данным (изображениям).Динамическая нарезка изображений, позволяет освободить место под все "нарезки" и не беспокоиться об их актуальности.Но в данном случае мы требовательны к вычислительным ресурсам и слой "кеширования" нарезки можно "сдвинуть" ближе к клиенту и в любой момент обновить набор размеров.То есть идеальным вариантом (для ресурсов) будет гибридная нарезка. Когда мы выделяем данные, которые нужно нарезать динамически или разово.Также если у Вас часто меняются размеры и формат изображений - Вам скорее больше подходит динамическая нарезка (при наличии вычислительных мощностей). Настоятельно рекомендую делать просчёты под Ваши планируемые требования и мощности.ПримечаниеВсе примеры кода приведены в ознакомительных целях. Соответственно, Вам нужно проверять и адаптировать данный подход под Ваши требования и цели.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, GTD, Лайфхаки для гиков, Мозг, Здоровье] Наша Зверская сущность
- [Разработка веб-сайтов, Программирование, Dart, Flutter] DartUP 2020: архитектура Dart VM, non-nullability в действии и Flutter для бизнеса
- [Программирование, Go] Шаблоны проектирования в Go — Абстрактная Фабрика (перевод)
- [Google Chrome] Google продляет поддержку Chrome для Windows 7 на шесть месяцев
- [Python, Разработка игр, Swift, Kotlin, Дизайн игр] MMORPG больше не в Telegram — Swift и Kotlin — Первый большой проект — Часть 1
- [Машинное обучение, Искусственный интеллект] В Google обучили нейросеть писать стихотворения в стиле известных поэтов
- [MySQL, Go] Пишем CRUD-приложение на Go с помощью Mysql, GORM, Echo, Clean Architecture (перевод)
- [JavaScript, Веб-аналитика, Интернет-маркетинг, Повышение конверсии, Поисковая оптимизация] Используем Google Tag Manager Server-Side вместо Zapier
- [Разработка веб-сайтов, JavaScript, Программирование] Введение в реактивное программирование
- [Веб-дизайн, Разработка веб-сайтов, Интерфейсы, Usability, Дизайн] 13 свежих и полезных дизайн-ресурсов уходящей осени
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_rabota_s_ikonkami (Работа с иконками), #_obrabotka_izobrazhenij (Обработка изображений), #_go, #_favicon, #_watermark, #_resizer, #_image, #_runtime, #_golang, #_placeholder, #_pixel, #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_rabota_s_ikonkami (
Работа с иконками
), #_obrabotka_izobrazhenij (
Обработка изображений
), #_go
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:06
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В Go есть возможность создавать файлы изображений. С помощью этого мы можем создавать картинки на лету (в runtime).Где же это может пригодится?Вот небольшой список того, что мы можем создать используя данный функционал:
func faviconHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "relative/path/to/favicon.ico") } ... func main() { http.HandleFunc("/favicon.ico", faviconHandler) }
import (
"bytes" "image" "image/gif" "net/http" ... ) var buf bytes.Buffer gif.Encode(&buf, image.Rect(0, 0, 16, 16), nil) w.Header().Set("Content-Type", "image/jpeg") w.Header().Set("Content-Length", strconv.Itoa(len(buf.Bytes()))) w.Write(buf.Bytes()) import (
"bytes" "image" "image/color" "image/draw" "image/jpeg" ... ) buf := new(bytes.Buffer) m := image.NewRGBA(image.Rect(0, 0, 16, 16)) clr := color.RGBA{B: 0, A: 0} draw.Draw(m, m.Bounds(), image.NewUniform(clr), image.Pointer{}, draw.Src) jpeg.Encode(buffer, img, nil) w.Header().Set("Content-Type", "image/jpeg") w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes()))) w.Write(buf.Bytes()) var buf bytes.Buffer
gif.Encode(&buf, image.Rect(0, 0, 1, 1, nil)) Для генерации placeholder-ов мы будем использовать тот же подход, что и для картинок с фиксированным размером, но с возможностью добавлять текст и изменять размеры картинки и шрифта.Во-первых, определимся с требованиями:
package colors
import ( "image/color" "strconv" ) type rgb struct { red uint8 green uint8 blue uint8 } func ToRGBA(h string) (color.RGBA, error) { rgb, err := hex2RGB(h) if err != nil { return color.RGBA{}, err } return color.RGBA{R: rgb.red, G: rgb.green, B: rgb.blue, A: 255}, nil } func hex2RGB(hex string) (rgb, error) { values, err := strconv.ParseUint(hex, 16, 32) if err != nil { return rgb{}, err } return rgb{ red: uint8(values >> 16), green: uint8((values >> 8) & 0xFF), blue: uint8(values & 0xFF), }, nil } Непосредственно пакет сборки нашей картинки: pacakge img
const ( // Параметры по умолчанию imgColorDefault = "E5E5E5" msgColorDefault = "AAAAAA" imgWDefault = 300 imgHDefault = 300 fontSizeDefault = 0 dpiDefault float64 = 72 fontfileDefault = "wqy-zenhei.ttf" ) // Do - входная точка. func Do(params []string) (*bytes.Buffer, error) { // fetch img params: imgW, imgH, text, etc // Соберём структуру Текста label := Label{Text: msg, FontSize: fontSize, Color: msgColor} // Соберём структуру Картинки с нужными полями - высота, ширина, цвет и текст img := Img{Width: imgW, Height: imgH, Color: imgColor, Label: label} // Сгенерим нашу картинку с текстом return img.generate() } // generate - соберёт картинку по нужным размерам, цветом и текстом. func (i Img) generate() (*bytes.Buffer, error) { // Если есть размеры и нет требований по Тексту - соберём Текст по умолчанию. if ((i.Width > 0 || i.Height > 0) && i.Text == "") || i.Text == "" { i.Text = fmt.Sprintf("%d x %d", i.Width, i.Height) } // Если нет требований по размеру шрифта - подберём его исходя из размеров картинки. if i.FontSize == 0 { i.FontSize = i.Width / 10 if i.Height < i.Width { i.FontSize = i.Height / 5 } } // Переведём цвет из строки в color.RGBA. clr, err := colors.ToRGBA(i.Color) if err != nil { return nil, err } // Создадим in-memory картинку с нужными размерами. m := image.NewRGBA(image.Rect(0, 0, i.Width, i.Height)) // Отрисуем картинку: // - по размерам (Bounds) // - и с цветом (Uniform - обёртка над color.Color c Image функциями) // - исходя из точки (Point), как базовой картинки // - заполним цветом нашу Uniform (draw.Src) draw.Draw(m, m.Bounds(), image.NewUniform(clr), image.Point{}, draw.Src) // Добавим текст в картинку. if err = i.drawLabel(m); err != nil { return nil, err } var im image.Image = m // Выделим память под нашу данные (байты картинки). buffer := &bytes.Buffer{} // Закодируем картинку в нашу аллоцированную память. err = jpeg.Encode(buffer, im, nil) return buffer, err } // drawLabel - добавит текст на картинку. func (i *Img) drawLabel(m *image.RGBA) error { // Разберём цвет текста из строки в RGBA. clr, err := colors.ToRGBA(i.Label.Color) if err != nil { return err } // Получим шрифт (должен работать и с латиницей и с кириллицей). fontBytes, err := ioutil.ReadFile(fontfileDefault) if err != nil { return err } fnt, err := truetype.Parse(fontBytes) if err != nil { return err } // Подготовим Drawer для отрисовки текста на картинке. d := &font.Drawer{ Dst: m, Src: image.NewUniform(clr), Face: truetype.NewFace(fnt, &truetype.Options{ Size: float64(i.FontSize), DPI: dpiDefault, Hinting: font.HintingNone, }), } // Зададим базовую линию. d.Dot = fixed.Point26_6{ X: (fixed.I(i.Width) - d.MeasureString(i.Text)) / 2, Y: fixed.I((i.Height+i.FontSize)/2 - 12), } // Непосредственно отрисовка текста в нашу RGBA картинку. d.DrawString(i.Text) return nil } http://localhost:8080/600/200/004620/Заглушка/FFFFFF/50
Обратите внимание: мы можем создавать изображение с текстом на латинице и/или на кириллице.А на такой запрос: http://localhost:8080/480/200
4. Наложение текста (watermark) на изображениеWatermark может пригодится в таких кейсах:
=========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_rabota_s_ikonkami ( Работа с иконками ), #_obrabotka_izobrazhenij ( Обработка изображений ), #_go |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:06
Часовой пояс: UTC + 5