Понадобилось мне при разработке мода на
7 Days to Die программно формировать изображение круга в зависимости от количества дней в игре. Так как я давно порывался посмотреть на
Go, решил я использовать его для данной задачи. Да и подходит он для такого микро-сервиса в самый раз.
Сделать веб сервер на
Go просто :) для этого используем стандартную библиотеку "
net/http"
func main() {
flag.Parse()
log.Println("Starting tiny webserver for 7DTD moon texture...")
log.Println("Document root path: ", *root)
http.HandleFunc("/moon", moonHandler)
log.Println("Listening on port: ", *port)
if err := http.ListenAndServe(":"+strconv.Itoa(*port), nil); err != nil {
log.Fatal("ERROR Listen and Serve: ", err)
}
}
Дальше, формируем изображение круга с использование библиотеки "
image/draw",
func moonHandler(w http.ResponseWriter, r *http.Request) {
d := getDay(r)
/*** Create colored image ***/
dst := image.NewRGBA(image.Rect(0, 0, cXY.X+cR+1, cXY.Y+cR+1))
draw.Draw(dst, dst.Bounds(), &circle{cXY, cR}, image.ZP, draw.Src)
writeImg(w, dst)
}
и отдаем ее как картинку "
image/png"
func writeImg(w http.ResponseWriter, img image.Image) {
buffer := new(bytes.Buffer)
if err := png.Encode(buffer, img); err != nil {
log.Println("Unable to encode image: ", err)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "image/png")
w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))
if _, err := w.Write(buffer.Bytes()); err != nil {
log.Println("Unable to write image: ", err)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
}
А теперь самое интересное, как же сделать эту картинку :)
Для этого описываем структуру:
type circle struct {
P image.Point
R int
}
И определяем методы которые описываю данную структуру в разрезе "
image.Image"
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), float64(y-c.P.Y), float64(c.R)
if xx*xx+yy*yy < rr*rr {
return color.Alpha{255}
}
return color.Alpha{0}
}
Все это прекрасно работает, но мне нужно было получить картинку с определенной прозрачностью, по этому пришлось немного изменить код функции "
At" и структуры "
circle". Добавляем поля для цвета и типа изображения (сплошная заливка или градиентная)
type circle struct {
P image.Point
R int
C color.NRGBA
S bool
}
... и теперь сама процедура отрисовки круга:
func (c *circle) At(x, y int) color.Color {
xx, yy, rr := float64(x-c.P.X), float64(y-c.P.Y), float64(c.R)
if xx*xx+yy*yy < rr*rr {
xr := math.Abs(float64(c.P.X - x))
yr := math.Abs(float64(c.P.Y - y))
ar := uint8((1 - math.Sqrt(xr*xr+yr*yr)/float64(c.R)) * float64(c.C.A))
if c.S {
ar = c.C.A
}
return color.NRGBA{c.C.R, c.C.G, c.C.B, ar}
}
return color.Alpha{0}
}
В итоге, мы можем получить два вида кружкок :) задав параметры при обращении к веб серверу.
|
|
|
|
Градиентная заливка для 7 дня |
Сплошная заливка для 7 дня |
Градиентная заливка для 5-6 дня |
Сплошная заливка для 5-6 дня |
Конечный результат как это все выглядит в игре :)