Работа с изображениями
В стандартной библиотеки Go много всего интересного. Например, пакет image
для работы с изображениями. Сейчас попробуем нарисовать что нить простенькое для понимания основных принципов.
Начало
Сообществом уже написана целая куча библиотек для работы с графикой. Но, чтобы продуктивно работать с этими инструментами, нужно разбираться, как это все устроено.
Начинаем с самого простого. Создадим простую png картинку абсолютно без ничего.
package main
import (
"fmt"
"image"
"image/png"
"os"
)
func main() {
file, err := os.Create("someimage.png")
if err != nil {
fmt.Errorf("%s", err)
}
img := image.NewRGBA(image.Rect(0, 0, 500, 500))
png.Encode(file, img)
}
img := image.NewRGBA(image.Rect(0, 0, 500, 500))
- создаем наше изображение, которое состоит из одной области размером 500x500 пикселей.
png.Encode(file, m)
- сохраняем изображение в файл. Стандартная библиотека поддерживает два формата изображений: png и jpeg.
Теперь заполним картинку цветом. Для этого нам нужны пакеты image/draw
и image/color
package main
import (
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
var teal color.Color = color.RGBA{0, 200, 200, 255}
func main() {
file, err := os.Create("someimage.png")
if err != nil {
fmt.Errorf("%s", err)
}
img := image.NewRGBA(image.Rect(0, 0, 500, 500))
draw.Draw(img, img.Bounds(), &image.Uniform{teal}, image.ZP, draw.Src)
// или draw.Draw(img, img.Bounds(), image.Transparent, image.ZP, draw.Src)
png.Encode(file, img)
}
var teal color.Color = color.RGBA{0, 200, 200, 255}
- цвет, которым будет заполнена наша картинка. Первое значение - это прозрачность.
draw.Draw(img, img.Bounds(), &image.Uniform{rectaleColor}, image.ZP, draw.Src)
- заполняем картинку цветом. Функция draw.Draw()
в параметрах принимает:
- Создаваемое изображение типа
draw.Image
, - Прямоугольную область рисования. У нас это все изображение
img.Bounds()
) - Исходное изображение
&image.Uniform{rectaleColor}
. Это изображение с бесконечными размерами и залитое цветомrectaleColor
- Координаты прямоугольника для рисования
image.ZP
- это точка с координатами (0, 0) draw.Src
- метод рисования, который указывает функции просто скопировать исходное изображение в целевое не применяя никаких преобразований. В нашем случае, просто окрасит пиксели изображения в нужный цвет.
Линии
Пришло время нарисовать пару линий.
// ...
var red color.Color = color.RGBA{200, 30, 30, 255}
// ...
draw.Draw(img, img.Bounds(), &image.Uniform{teal}, image.ZP, draw.Src)
for x := 20; x < 380; x++ {
y := x/3 + 15
img.Set(x, y, red)
}
// ...
Линия начинается в точке с координатами x = 20 y = 21 и заканчивается в точке x = 379 y = 141.
y := x/3 + 15
- простейшая математическая функция рисования линии на координатной плоскости. Меняя тройку - меняем наклон линии.
img.Set(x, y, red)
- функция, которая заполняет определенную точку на изображении указанным цветом.
Фигуры
Линии это просто. Нам нужно больше математики! Будем рисовать круги. Создаем новый тип Circle
type Circle struct {
p image.Point
r int
}
func (c *Circle) ColorModel() color.Model {
return color.AlphaModel
}
func (c *Circle) Bounds() image.Rectangle {
return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}
func (c *Circle) At(x, y int) color.Color {
xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
if xx*xx+yy*yy < rr*rr {
return color.Alpha{255}
}
return color.Alpha{0}
}
Тип Circle
определяет интерфейс image.Image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
ColorModel() color.Model
- определяет какая цветовая модель будет использоваться для нашего изображения.
Bounds() Rectangle
- определяем границы, в переделах которых будет размещена наша фигура.
At(x, y int) color.Color
- возвращет цвет для определенных координат.
Теперь нам нужно изображение, которое будет исходником для нашей маски:
mask := image.NewRGBA(image.Rect(0, 0, 500, 500))
draw.Draw(mask, mask.Bounds(), &image.Uniform{red}, image.ZP, draw.Src)
Это изображение заполнено красным цветом.
Рисуем три круга используя функцию draw.DrawMask(), которая принимает аж 7 параметров.
draw.DrawMask(img, img.Bounds(), mask, image.ZP, &Circle{image.Point{20, 21}, 20}, image.ZP, draw.Over)
draw.DrawMask(img, img.Bounds(), mask, image.ZP, &Circle{image.Point{379, 141}, 20}, image.ZP, draw.Over)
draw.DrawMask(img, img.Bounds(), mask, image.ZP, &Circle{image.Point{239, 321}, 70}, image.ZP, draw.Over)
Сигнатура функции draw.DrawMask
выглядит так:
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)
dst Image
- это изображение, на которое мы будем накладывать маскуr image.Rectangle
- границы изображения, в котором сохранятся результаты преобразованияsrc image.Image
- это исходное изображение, которое будет использоваться для создания маски.sp image.Point
- позиция исходного изображенияmask image.Image
- изображение, которое будет играть роль маски. У нас это&Circle{image.Point{x, y}, r}
mp image.Point
- позиция для создаваемой маскиop Op
- оператор композиции изображения. В нашем случае draw.Over.
Собираем все это вместе и запускаем. В результате у нас получится вот такая картинка:
Конечный вариант этой программы можно найти на гисте.
Почитать по теме: