Tvorba 2D i 3D grafiky a animací v Go s využitím projektu GoGPU

14. 7. 2026
Doba čtení: 49 minut

Sdílet

GoGPU
Autor: Andrey Kolkov
Jazyk Go se typicky používá v cloudových službách, pro vývoj back endu, nástrojů ovládaných z příkazové řádky atd. Tato situace se do značné míry změnila s příchodem relativně mladého projektu GoGPU.

Programovací jazyk Go se používá v mnoha oblastech – cloudové služby a platformy, vývoj back endu, nástroje ovládané z příkazové řádky, nástroje pro CI/CD apod. Go se ovšem z historických důvodů příliš často nepoužíval pro vývoj klasických desktopových aplikací (pouze pro jejich imitaci přes Electron). Poměrně dlouhou dobu totiž nebyly k dispozici vhodné nástroje a knihovny. Tato situace se do značné míry změnila s příchodem (relativně mladého) projektu nazvaného GoGPU, jenž je určen pro tvorbu desktopových aplikací s 2D a popř. i 3D grafikou.

Co se dozvíte v článku
  1. Knihovna gg – ale která?
  2. Instalace knihovny GoGPU/gg
  3. Inicializace grafického kontextu a smazání kreslicí plochy
  4. Vymazání plochy obrázku se specifikací použité barvy
  5. Nastavení barvy kreslení bez specifikace průhlednosti i se specifikací průhlednosti
  6. Barvový prostor RGB a RGBA
  7. Specifikace šířky vykreslovaných obrysů
  8. Styl ukončení křivek i úseček, styl vykreslování (čárkované a čerchované stopy)
  9. Práce s cestami (paths)
  10. Otevřené a uzavřené cesty
  11. Složitější cesty založené na kvadratických a kubických Bézierových křivkách
  12. Vykreslení kvadratických Bézierových křivek
  13. Vykreslení kubických Bézierových křivek
  14. Rotace Bézierových křivek s využitím transformace zadané metodou RotateAbout
  15. Převod sekvence snímků na animaci ve formátu GIF
  16. Lineární transformace aplikované při vykreslování 2D tvarů
  17. Práce s fonty, vykreslení textu
  18. Repositář s demonstračními příklady
  19. Odkazy na Internetu

Jak je ovšem v ekosystému jazyka Go dobrým zvykem, není GoGPU navržen jako všeobjímající framework, který do značné míry určuje architekturu aplikací. Je tomu přesně naopak, protože GoGPU je „jen“ sadou knihoven, které lze v případě potřeby používat samostatně (což si ostatně ukážeme i dnes):

Balíček Stručný popis
gogpu jednoduchý grafický framework, integruje další balíčky (dnes ho nepoužijeme)
gg vykreslování rastrové 2D grafiky jak softwarově, tak pomocí GPU, téma dnešního článku
gg-pdf export vykreslené rastrové 2D grafiky do formátu PDF, rozšiřuje možnosti balíčku gg
gg-svg export vykreslené rastrové 2D grafiky do formátu SVG, rozšiřuje možnosti balíčku gg
g3d vykreslování 3D grafiky jak softwarově, tak pomocí GPU, de facto alternativa ke klasickému OpenGL
gputypes pomocný balíček s typy WebGPU, prozatím ho nepoužijeme
ui tvorba grafického uživatelského rozhraní
editor nový (a nedokončený!) balíček – bude se jednat o komponentu pro tvorbu textových vstupních polí
systray manipulace s obsahem hlavního panelu Windows a některých Linuxových desktopových prostředí
audio práce se zvukem (popíšeme si později)

Jak již bylo napsáno výše, je možné jednotlivé balíčky používat samostatně. Příkladem je back end služba, která generuje rastrové obrázky popř. SVG kresby. V takových případech si vystačíme s gg nebo gg-svg. Naproti tomu jednoduchá aplikace s grafickým uživatelským rozhraním nebude vyžadovat g3d atd. Taktéž je možné vykreslování provádět jak softwarově, tak i s využitím GPU (výsledky ovšem mohou být nepatrně odlišné).

Poznámka: některé balíčky jsou relativně nové, ovšem všechny vlastnosti popsané v tomto článku i v článcích navazujících se budou týkat stabilního API.

Knihovna gg – ale která?

Z předchozí kapitoly je zřejmé, že se projekt GoGPU skládá z mnoha balíčků, které za sebou mají různou historii vývoje. Týká se to mj. i balíčku, který se jmenuje gg. Čtenáři s lepší pamětí než autor tohoto článku (tj. všichni čtenáři) si možná vzpomenou, že jsme se s balíčkem gg v seriálu o programovacím jazyce Go již kdysi setkali. Ovšem jednalo se tehdy o balíček fogleman/gg, nikoli o gogpu/gg. Oba balíčky mají podobné, ovšem nikoli totožné API. Dnes se pochopitelně zaměříme na druhý z těchto balíčků, tedy gogpu/gg, který je v některých ohledech odlišný, ovšem navíc nabízí propojení s balíčky go-pdf, go-svg a taktéž ui. V dalším textu tedy budeme pod názvem gg vždy myslet balíček gogpu/gg.

Poznámka: jen na okraj je dobré si povšimnout, jak dobře navržený je v ekosystému programovacího jazyka Go systém pojmenování balíčků. I v případě shody jmen nedojde k žádným problémům, dokonce ani tehdy ne, pokud bychom chtěli jak fogleman/gg, tak i gogpu/gg použít současně.

Instalace knihovny GoGPU/gg

V praktické části dnešního článku se – prozatím ovšem pouze v rychlosti a bez dalších podrobností – seznámíme s některými možnostmi, které nám v oblasti 2D grafiky poskytuje knihovna nazvaná GoGPU/gg (a tedy balíček gogpu/gg). Možnosti poskytované touto knihovnou do jisté míry připomínají knihovnu Cairo popř. OpenVG a EGL: 2D scéna je při použití této knihovny vykreslována v immediate (přímém) režimu, přičemž se používá koncept takzvaných cest (path), stylů vykreslování i vyplňování a taktéž lineárních transformací, které je možné v případě potřeby skládat. Navíc tato knihovna podporuje rasterizaci TrueType fontů, takže je možné do obrázků vkládat různě transformovaný text (lineární transformace se pochopitelně vztahují i na text).

Pozor je nutné si dát především na to, že GoGPU vyžaduje relativně novou verzi jazyka Go, konkrétně Go 1.25. Pokud máte nainstalovánu starší verzi Go, je čas na upgrade:

$ sudo dnf upgrade golang
 
Updating and loading repositories:
Repositories loaded.
Package                  Arch    Version                  Repository        Size
Upgrading:
 golang                  x86_64  1.25.10-2.fc42           updates        9.6 MiB
   replacing golang      x86_64  1.24.10-1.fc42           updates        8.9 MiB
 golang-bin              x86_64  1.25.10-2.fc42           updates       67.7 MiB
   replacing golang-bin  x86_64  1.24.10-1.fc42           updates      122.1 MiB
 golang-misc             noarch  1.25.10-2.fc42           updates       56.3 KiB
   replacing golang-misc noarch  1.24.10-1.fc42           updates       60.2 KiB
 golang-src              noarch  1.25.10-2.fc42           updates       81.5 MiB
   replacing golang-src  noarch  1.24.10-1.fc42           updates       79.2 MiB
 
Transaction Summary:
 Upgrading:          4 packages
 Replacing:          4 packages
 
Total size of inbound packages is 33 MiB. Need to download 33 MiB.
After this operation, 51 MiB will be freed (install 159 MiB, remove 210 MiB).
Is this ok [y/N]:

Po upgrade na novější verzi Go se přesvědčíme o tom, že je k dispozici alespoň Go 1.25:

$ go version
 
go version go1.25.10 X:nodwarf5 linux/amd64

Samotná instalace knihovny gogpu/gg je stejná, jako v případě jiných externích balíčků, což konkrétně znamená, že se použije příkaz go get spuštěný v adresáři s projektem:

$ go get github.com/gogpu/gg
 
go: downloading github.com/gogpu/gg v0.50.1
go: downloading github.com/gogpu/gpucontext v0.21.0
go: downloading golang.org/x/image v0.43.0
go: downloading golang.org/x/text v0.38.0
go: downloading github.com/gogpu/gputypes v0.5.1
go: upgraded go 1.24.10 => 1.25.0
go: added github.com/gogpu/gg v0.50.1
go: added github.com/gogpu/gpucontext v0.21.0
go: added github.com/gogpu/gputypes v0.5.1
go: added golang.org/x/image v0.43.0
go: added golang.org/x/text v0.38.0

Projektový soubor by měl nyní mít zhruba následující obsah:

module gg-demos
 
go 1.25.0
 
require (
        github.com/gogpu/gg v0.50.1 // indirect
        github.com/gogpu/gpucontext v0.21.0 // indirect
        github.com/gogpu/gputypes v0.5.1 // indirect
        golang.org/x/image v0.43.0 // indirect
        golang.org/x/text v0.38.0 // indirect
)

Konkrétní hashe použitých balíčků jsou, jak je zvykem, uloženy v souboru go.sum:

github.com/gogpu/gg v0.50.1 h1:6Xb+f68mw6YVOtBu0n7e4INIXE4NUy6+fvNucCMqgH8=
github.com/gogpu/gg v0.50.1/go.mod h1:vdfjSLVXNp7Qx9DLH/NRZqvLiQniKhHRFN/JkSF3LJk=
github.com/gogpu/gpucontext v0.21.0 h1:O1SnXBiednIyVP4Zx5SASh0svYiG6g/wF1igNZVo1Pc=
github.com/gogpu/gpucontext v0.21.0/go.mod h1:6zwdmYXH5GQltoiHbb3WXVS/UJ5bFsCux0mXCVqGlzY=
github.com/gogpu/gputypes v0.5.1 h1:X38OPcP6umQqqubzzJYL6Nm1tXHSNQj6TRSAoxdAJmg=
github.com/gogpu/gputypes v0.5.1/go.mod h1:cnXrDMwTpWTvJLW1Vreop3PcT6a2YP/i3s91rPaOavw=
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=

Mimochodem, pokud budete s využitím GoGPU (a balíčku g3d) vytvářet 3D grafiku i s grafickým uživatelským rozhraním, budou závislosti odlišné (ale to již předbíháme):

module gg-demos-3D
 
go 1.25.10
 
require (
        github.com/go-webgpu/goffi v0.5.6 // indirect
        github.com/go-webgpu/webgpu v0.5.2 // indirect
        github.com/gogpu/g3d v0.1.1 // indirect
        github.com/gogpu/gogpu v0.44.1 // indirect
        github.com/gogpu/gpucontext v0.21.0 // indirect
        github.com/gogpu/gputypes v0.5.1 // indirect
        github.com/gogpu/naga v0.17.15 // indirect
        github.com/gogpu/wgpu v0.30.10 // indirect
        golang.org/x/sys v0.46.0 // indirect
)

Soubor go.sum bude v tomto případě rozsáhlejší:

github.com/go-webgpu/goffi v0.5.5 h1:xLWMhnJq+GrejlhTC3iVt3q8ozRbmYSq8kzuw6MKlgE=
github.com/go-webgpu/goffi v0.5.5/go.mod h1:wfoxNsJkU+5RFbV1kNN1kunhc1lFHuJKK3zpgx08/uM=
github.com/go-webgpu/goffi v0.5.6 h1:jAN4ciR63DcpU/qdX4A3wbNvcpZ6AdFK+H7OB/1+9Lc=
github.com/go-webgpu/goffi v0.5.6/go.mod h1:wfoxNsJkU+5RFbV1kNN1kunhc1lFHuJKK3zpgx08/uM=
github.com/go-webgpu/webgpu v0.5.2 h1:E+yKCbRw/Dgn77pb/CX3kXatwwROGX7/B4ZFbRPaRbQ=
github.com/go-webgpu/webgpu v0.5.2/go.mod h1:d2RR3tI2kUtcMfpC+qJt46IRkDslRhftjCfQvnprGkY=
github.com/gogpu/g3d v0.1.1 h1:aQHP5zQ0mlfYWSJUf4r+0R1cJ1dfdnOC7YLK3e3hNpU=
github.com/gogpu/g3d v0.1.1/go.mod h1:7L7xlaG56S3FzfaMI9s/ww+OXsR+9oHsCwJHnME9TlE=
github.com/gogpu/gogpu v0.44.1 h1:oGTO4DpKCLL5huwe6w64CNWfPBZlAueTOd5xx2xfO5c=
github.com/gogpu/gogpu v0.44.1/go.mod h1:woyKt98CmzLTCbklx0pPNBH/rSxQVzcH1y+qABuZQtg=
github.com/gogpu/gpucontext v0.21.0 h1:O1SnXBiednIyVP4Zx5SASh0svYiG6g/wF1igNZVo1Pc=
github.com/gogpu/gpucontext v0.21.0/go.mod h1:6zwdmYXH5GQltoiHbb3WXVS/UJ5bFsCux0mXCVqGlzY=
github.com/gogpu/gputypes v0.5.0 h1:i2ED/9w6m6yLxf8XJT69/NIMSNTLO2y5F1LqvugCKIE=
github.com/gogpu/gputypes v0.5.0/go.mod h1:cnXrDMwTpWTvJLW1Vreop3PcT6a2YP/i3s91rPaOavw=
github.com/gogpu/gputypes v0.5.1 h1:X38OPcP6umQqqubzzJYL6Nm1tXHSNQj6TRSAoxdAJmg=
github.com/gogpu/gputypes v0.5.1/go.mod h1:cnXrDMwTpWTvJLW1Vreop3PcT6a2YP/i3s91rPaOavw=
github.com/gogpu/naga v0.17.15 h1:uyy4bc8wYlvDaYOpmCBYrJ+d+e7ZqP2BN36csmRVs7o=
github.com/gogpu/naga v0.17.15/go.mod h1:15sQaHKkbqXcwTN+hHYGLsA0WBBnkmYzne/eF5p5WEg=
github.com/gogpu/wgpu v0.30.2 h1:7csTEfCvQnrQHMxZaUB6ulxgsCmrg2eVJSU4demI1hQ=
github.com/gogpu/wgpu v0.30.2/go.mod h1:Ih63YKckUGtNXnXT6LjkHcH6FUfib0TjCwDR5615gPI=
github.com/gogpu/wgpu v0.30.10 h1:KWQOo2RR8d+uNFYYD5WGCG0f4AiX8IOHH8kWi2w6Eec=
github.com/gogpu/wgpu v0.30.10/go.mod h1:9TDtAhJmIhh1RfxcLySWEtkwfFMVf0Y7X7TLtVqvTBE=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=

V adresáři s projektem si můžeme zobrazit nápovědu k právě nainstalovanému balíčku gogpu/gg:

$ go doc gogpu/gg
 
package gg // import "github.com/gogpu/gg"
 
Package gg provides a simple 2D graphics library for Go.
 
# Overview
 
gg is a Pure Go 2D graphics library inspired by fogleman/gg and designed to
integrate with the GoGPU ecosystem. It provides an immediate-mode drawing API
similar to HTML Canvas, with both software and GPU rendering backends.
 
# Quick Start
 
    import "github.com/gogpu/gg"
 
    // Create a drawing context (dc = drawing context convention)
    dc := gg.NewContext(512, 512)
 
    // Draw shapes
    dc.SetRGB(1, 0, 0)
    dc.DrawCircle(256, 256, 100)
    dc.Fill()
 
    // Save to PNG
    dc.SavePNG("output.png")
 
# API Compatibility
 
The API is designed to be compatible with fogleman/gg for easy migration.
Most fogleman/gg code should work with minimal changes.

Inicializace grafického kontextu a smazání kreslicí plochy

V knihovně gg se při vykreslování 2D scény pracuje s takzvaným grafickým kontextem, jenž se získá konstruktorem NewContext. Kontext je datová struktura obsahující reference jak na vznikající rastrový obrázek, tak i na parametry vykreslování. Z hlavičky konstruktoru je patrné, že je mu nutné předat alespoň rozměry kreslicí plochy (canvasu), které budou v případě rasterizace výsledků odpovídat rozlišení (v pixelech) :

func NewContext(width, height int, opts ...ContextOption) *Context

Ve výchozím nastavení jsou všechny pixely rastrového obrázku průhledné, ovšem celý obraz můžeme vyplnit jakoukoli jinou barvou. Vymazání lze (v libovolný okamžik) zajistit metodou nazvanou příznačně Clear:

func (c *Context) Clear()
    Clear resets the entire context to transparent (zero alpha). To fill with a
    specific background color, use [ClearWithColor].

Výsledný rastrový obrázek po jeho smazání necháme uložit do souboru ve formátu PNG metodou SavePNG:

func (c *Context) SavePNG(path string) error
    SavePNG saves the context to a PNG file.

Dnešní první demonstrační příklad po svém spuštění vytvoří soubor 01.png s následujícím (přímo neviditelným) obsahem:

Obrázek s průhlednými pixely (výchozí stav).

Obrázek 1: Obrázek s průhlednými pixely (výchozí stav).

Autor: tisnik, podle licence: Rights Managed

Následuje výpis úplného zdrojového kódu prvního demonstračního příkladu, který tento obrázek vytvoří:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 1:
//    Balíček gogpu/gg.
//    Vymazání obsahu rastrového obrázku.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "01.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // smazání obsahu obrázku s využitím kontextu
        dc.Clear()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Ve druhém příkladu je realizováno vymazání celého obrázku bílou barvou. Musíme použít tři metody. První z nich „zaregistruje“ vykreslení obdélníka. Sice poněkud předbíháme, ale povšimněte si, že souřadnice i rozměry nejsou celými čísly:

func (c *Context) DrawRectangle(x, y, w, h float64)
    DrawRectangle draws a rectangle.

Další metoda SetRGB slouží pro nastavení barvy kreslení či výplně:

func (c *Context) SetRGB(r, g, b float64)
    SetRGB sets the current color using RGB values (0-1).

A konečně metoda Fill vyplní uzavřený tvar (což obdélník je) zvolenou barvou:

func (c *Context) Fill() error
    Fill fills the current path and clears it. If a GPU accelerator is
    registered and supports the path, it is used first. Otherwise, the software
    renderer handles the operation. The RasterizerMode set via SetRasterizerMode
    controls algorithm selection. Returns an error if the rendering operation
    fails.

Výsledkem je bílý obrázek:

Obrázek s bílými pixely s explicitně nastavenou průhledností.

 Obrázek 2: Obrázek s bílými pixely s explicitně nastavenou průhledností.

Autor: tisnik, podle licence: Rights Managed

Následuje výpis úplného zdrojového kódu druhého demonstračního příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 2:
//    Balíček gogpu/gg.
//    Vymazání obrázku vykreslením vyplněného obdélníku.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "02.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // vykreslování s využitím kontextu
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)

Vymazání plochy obrázku se specifikací použité barvy

Pokusme se nyní nastavit barvu vykreslování či výplně a poté zavolat metodu Clear:

// vykreslování s využitím kontextu
dc.SetRGB(1.0, 1.0, 1.0)
dc.Clear()

Výsledkem bude – možná poněkud překvapivě – obrázek s průhlednými pixely:

Obrázek s průhlednými pixely.

Obrázek 3: Obrázek s průhlednými pixely (nejsou ovlivněny nastavenou barvou).

Autor: tisnik, podle licence: Rights Managed

Opět si pochopitelně uvedeme úplný zdrojový kód:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 3:
//    Balíček gogpu/gg.
//    Vymazání obrazovky (lepší varianta).
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "03.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // vykreslování s využitím kontextu
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Clear()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Aby byla plocha obrázky vymazána zvolenu barvou, musí se namísto metody Context.Clear zavolat metoda se jménem Context.ClearWithColor:

func (c *Context) ClearWithColor(col RGBA)
    ClearWithColor fills the entire context with the specified color. This is
    the recommended way to set a background color before drawing.

Korektní postup může využívat barvu reprezentovanou strukturou gg.RGBA:

type RGBA struct {
        R, G, B, A float64
}
    RGBA represents a color with red, green, blue, and alpha components.
    Each component is in the range [0, 1].

To si pochopitelně můžeme velmi snadno otestovat:

// vykreslování s využitím kontextu
color := gg.RGBA{R: 0.5, G: 0.9, B: 0.0, A: 0.5}
dc.ClearWithColor(color)

Výsledek:

Explicitně nastavená barva pixelů při smazání plochy obrázku.

 Obrázek 4: Explicitně nastavená barva pixelů při smazání plochy obrázku.

Autor: tisnik, podle licence: Rights Managed

Následuje výpis úplného zdrojového kódu příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 4:
//    Balíček gogpu/gg.
//    Vymazání obrazovky (varianta se specifikací barvy).
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "04.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // vykreslování s využitím kontextu
        color := gg.RGBA{R: 0.5, G: 0.9, B: 0.0, A: 0.5}
        dc.ClearWithColor(color)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Nastavení barvy a následné vymazání celé plochy obrázku lze zkrátit na jediný řádek:

// vykreslování s využitím kontextu
dc.ClearWithColor(gg.RGBA{R: 0.5, G: 0.9, B: 0.0, A: 0.5})

S výsledkem:

Explicitně nastavená barva pixelů při smazání plochy obrázku.

 Obrázek 5: Explicitně nastavená barva pixelů při smazání plochy obrázku.

Autor: tisnik, podle licence: Rights Managed

Zdrojový kód v pořadí již pátého demonstračního příkladu vypadá takto:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 5:
//    Balíček gogpu/gg.
//    Vymazání obrazovky (varianta se specifikací barvy).
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "05.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // vykreslování s využitím kontextu
        dc.ClearWithColor(gg.RGBA{R: 0.5, G: 0.9, B: 0.0, A: 0.5})
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Nastavení barvy kreslení bez specifikace průhlednosti i se specifikací průhlednosti

Barva použitá pro vykreslování a/nebo pro výplně je reprezentována v barvovém prostoru RGB, ovšem navíc je k ní přidána i informace o alfa kanálu (tedy průhlednost). Změna aktuálně nastavené barvy se v tom nejjednodušším případě (pokud nepotřebujeme například využít HSL) provádí metodou Context.SetRGB (tu jsme již použili) nebo Context.SetRGBA. Barvové složky i průhlednost jsou reprezentovány hodnotami se znaménkem s rozsahem 0,0 až 1,0:

func (c *Context) SetRGB(r, g, b float64)
    SetRGB sets the current color using RGB values (0-1).

a:

func (c *Context) SetRGBA(r, g, b, a float64)
    SetRGBA sets the current color using RGBA values (0-1).

Další demonstrační příklad vykreslí vzorník barev, u nichž se mění jejich červená a modrá složka, zatímco složka zelená je nastavená na 50%:

Specifikace barvy vykreslování a výplně.

Obrázek 6: Specifikace barvy vykreslování a výplně.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód tohoto příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 6:
//    Balíček gogpu/gg.
//    Práce s barvami.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 256
        const height = 256
 
        // jméno souboru s výsledným obrázkem
        const fileName = "06.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        for y := range 16 {
                for x := range 16 {
                        // nastavení barvy kreslení
                        dc.SetRGB(float64(x)/16.0, 0.5, float64(y)/16.0)
                        // vykreslení vyplněného obdélníka
                        dc.DrawRectangle(float64(x*16)+3, float64(y*16)+3, 10, 10)
                        dc.Fill()
                }
        }
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Zajímavé bude zjistit, jakým způsobem se vykreslí černé čtverce, u kterých se postupně mění alfa složka od 0,0 do 1,0. Přesně tuto operaci provádíme v pořadí již sedmém demonstračním příkladu:

Specifikace průhlednosti barvy vykreslování a výplně.

Obrázek 7: Specifikace průhlednosti barvy vykreslování a výplně.

Autor: tisnik, podle licence: Rights Managed

Zdrojový kód tohoto příkladu má následující tvar:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 7:
//    Balíček gogpu/gg.
//    Práce s barvami.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 256
        const height = 256
 
        // jméno souboru s výsledným obrázkem
        const fileName = "07.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        for y := range 16 {
                for x := range 16 {
                        // nastavení barvy kreslení
                        dc.SetRGBA(0.0, 0.0, 0.0, float64(x)/32.0+float64(y)/32.0)
                        // vykreslení vyplněného obdélníka
                        dc.DrawRectangle(float64(x*16)+3, float64(y*16)+3, 10, 10)
                        dc.Fill()
                }
        }
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Barvový prostor RGB a RGBA

Nastavení průhlednosti (alfa složky) na hodnotu 1,0 znamená, že barva bude při vykreslování zcela neprůhledná:

go_gg_1

Obrázek 8: Specifikace alfa složky barvy při vykreslování.

Autor: tisnik, podle licence: Rights Managed

V dnešním osmém demonstračním příkladu, je použita programová smyčka, která po svém spuštění vykreslí tři řady úseček, pokaždé s odlišnou barvou:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 8:
//    Balíček gogpu/gg.
//    Barvový prostor RGB a RGBA.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "08.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        for i := 0; i < 256; i += 4 {
                x := float64(i + 32)
 
                // barvové složky jsou hodnoty v rozsahu 0.0 až 1.0
                r := float64(i) / 256.0
                dc.SetRGBA(r, 0.0, 0.0, 1.0)
                dc.DrawLine(x, 20, x, 75)
                dc.Stroke()
 
                g := float64(i) / 256.0
                dc.SetRGBA(0.0, g, 0.0, 1.0)
                dc.DrawLine(x, 92, x, height-92)
                dc.Stroke()
 
                b := float64(i) / 256.0
                dc.SetRGBA(0.0, 0.0, b, 1.0)
                dc.DrawLine(x, height-75, x, height-20)
                dc.Stroke()
        }
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

V případě, že se namísto metody SetRGB použije pro nastavení barvy vykreslování metoda nazvaná SetRGBA, je možné, jak již víme, specifikovat i průhlednost vykreslovaných pixelů. I hodnota průhlednosti (alfa kanál) většinou leží v rozsahu od 0,0 do 1,0, takže je možné použít například následující úryvek kódu určený pro vykreslení třiceti dvou svislých úseček, každou se stejnou barvou (v tomto případě černou barvou), ale s odlišnou průhledností. Úsečka zcela nalevo je na 100% průhledná, zatímco úsečka napravo je neprůhledná:

for i := 0; i < 256; i += 8 {
        x := float64(i + 32)
        // průhlednost je hodnota v rozsahu 0.0 až 1.0
        alpha := float64(i) / 256.0
        dc.SetRGBA(0.0, 0.0, 0.0, alpha)
 
        dc.DrawLine(x, 20, x, height/2-20)
        dc.Stroke()
}

Ve skutečnosti ovšem bude demonstrační příklad, na němž bude ukázána práce s průhledností, nepatrně složitější, neboť si ukážeme, jaký vliv má hodnota alfa pro černé úsečky kreslené na bílém pozadí i pro bílé úsečky kreslené na pozadí černém. Pozadí má přitom v obou případech (bez ohledu na barvu) nastavenou průhlednost na nulu, takže by měl výsledek vypadat následovně:

Specifikace alfa složky barvy při vykreslování.

Obrázek 9: Specifikace alfa složky barvy při vykreslování.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 9:
//    Balíček gogpu/gg.
//    Barvový prostor RGB a RGBA.
//    Alfa kanál v barvovém prostoru RGBA.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "09.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height/2)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        // pozadí obrázku
        dc.DrawRectangle(0, height/2, width, height)
        dc.SetRGB(0.0, 0.0, 0.0)
        dc.Fill()
 
        for i := 0; i < 256; i += 8 {
                x := float64(i + 32)
 
                // průhlednost je hodnota v rozsahu 0.0 až 1.0
                alpha := float64(i) / 256.0
                dc.SetRGBA(0.0, 0.0, 0.0, alpha)
 
                dc.DrawLine(x, 20, x, height/2-20)
 
                dc.Stroke()
 
                dc.SetRGBA(1.0, 1.0, 1.0, alpha)
                dc.DrawLine(x, height/2+20, x, height-20)
                dc.Stroke()
        }
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Specifikace šířky vykreslovaných obrysů

Při vykreslování obrysů popř. i jednotlivých úseček (či oblouků, cest atd.) je možné modifikovat i šířku stopy použité při rasterizaci a renderingu. Ta se udává – ostatně podobně jako naprostá většina ostatních numerických hodnot v knihovně GG – datovým typem float64, protože je použita subpixelová přesnost a navíc může být šířka modifikována nastavením měřítka odlišného od hodnoty 1,00. Pro nastavení šířky obrysů se nejčastěji používá metoda nazvaná SetLineWidth:

func (c *Context) SetLineWidth(width float64)
    SetLineWidth sets the line width for stroking.

Použití této metody je přímočaré:

for i := 0; i < 256; i += 16 {
        width := float64(i) / 20
        dc.SetLineWidth(width)
 
        x := float64(i + 32)
        dc.DrawLine(x, 20, x, height-20)
 
        dc.Stroke()
}

Samozřejmě si opět ukážeme, jakým způsobem se tato metoda použije v uceleném příkladu, který po svém spuštění vykreslí následující obrázek:

Modifikace šířky pera.

Obrázek 10: Modifikace šířky stopy.

Autor: tisnik, podle licence: Rights Managed

Poznámka: povšimněte si, jakým způsobem jsou stopy úseček ukončeny i toho, že u širších úseček stopa končí až za zadaným koncovým bodem (vrcholem).

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 9:
//    Balíček gogpu/gg.
//    Nastavení šířky vykreslovaných úseček a křivek.
//
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "10.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        for i := 0; i < 256; i += 16 {
                width := float64(i) / 20
                dc.SetLineWidth(width)
 
                x := float64(i + 32)
                dc.DrawLine(x, 20, x, height-20)
 
                dc.Stroke()
        }
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Styl ukončení křivek i úseček, styl vykreslování (čárkované a čerchované stopy)

U otevřených geometrických tvarů, tj. především u cest a lomených čar, je možné specifikovat i způsob jejich zakončení. Uzavřené tvary, tj. kružnice, elipsy, obdélníky či obecné mnohoúhelníky žádný jasně definovaný konec nemají, proto na ně nebude mít nastavení stylu ukončení žádný význam. V knihovně GG, podobně jako v mnoha dalších podobně koncipovaných knihovnách (ostatně i v SVG) je možné specifikovat tři způsoby ukončení stopy otevřených tvarů, které jsou specifikovány datovým typem LineCap odvozeného od základního celočíselného datového typu int:

Hodnota Význam
LineCapRound ukončení obrysu či cesty kruhovým obloukem (obecně dochází k prodloužení tvaru)
LineCapButt ukončení obrysu či cesty kolmým řezem v místě koncových bodů (tvar končí přesně na koncovém bodu/vrcholu)
LineCapSquare ukončení obrysu či cesty kolmým řezem vzdáleným od koncových bodů o 1/2 šířky cesty (dochází tak opět k prodloužení tvaru)

Pro nastavení stylu ukončení je možné použít buď metodu nazvanou jednoduše SetLineCap, které se potřebný styl předá v parametru:

func (dc *Context) SetLineCap(lineCap LineCap)

V dalším demonstračním příkladu je vliv nastaveného stylu ukončení ukázán na úsečkách s rozdílnou šířkou stopy. Povšimněte si vodorovných úseček, které naznačují, na kterém místě leží koncové body (vrcholy) – je patrné, že v některých případech je tvar úsečky skutečně přetažen přes koncový bod:

Modifikace stylu zakončení kreslené stopy.

Obrázek 11: Modifikace stylu zakončení kreslené stopy.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 11:
//    Balíček gogpu/gg.
//    Nastavení stylu konců úseček.
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "11.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1.0)
 
        dc.SetLineWidth(10.0)
 
        for i := 0; i < 256; i += 16 {
                x := float64(i + 32)
 
                width := float64(i) / 20
                dc.SetLineWidth(width)
 
                // nastavení stylů obou konců úsečky
                dc.SetLineCap(gg.LineCapRound)
                dc.DrawLine(x, 20, x, 75)
                dc.Stroke()
 
                // nastavení stylů obou konců úsečky
                dc.SetLineCap(gg.LineCapButt)
                dc.DrawLine(x, 92, x, height-92)
                dc.Stroke()
 
                // nastavení stylů obou konců úsečky
                dc.SetLineCap(gg.LineCapSquare)
                dc.DrawLine(x, height-75, x, height-20)
                dc.Stroke()
        }
 
        dc.SetRGBA(1.0, 0.0, 0.0, 1.0)
        dc.SetLineWidth(1.0)
 
        dc.DrawLine(32, 20, width-32, 20)
        dc.Stroke()
 
        dc.DrawLine(32, 75, width-32, 75)
        dc.Stroke()
 
        dc.DrawLine(32, 92, width-32, 92)
        dc.Stroke()
 
        dc.DrawLine(32, height-92, width-32, height-92)
        dc.Stroke()
 
        dc.DrawLine(32, height-75, width-32, height-75)
        dc.Stroke()
 
        dc.DrawLine(32, height-20, width-32, height-20)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Při kresbě liniových obrazců nebo okrajů plošných obrazců je možné zvolit vzorek (styl) úseček – plná (výchozí nastavení), čárkovaná, čerchovaná, střídavá apod. K nastavení vzorku vykreslení úsečky slouží metoda se jménem SetDash. Parametrem této metody je seznam délek vykreslených segmentů prokládaných délkami segmentů nevykreslených:

func (dc *Context) SetDash(dashes ...float64)

Nastavit je možné i posun (offset) celého vzorku, a to konkrétně metodou SetDashOffset:

func (dc *Context) SetDashOffset(offset float64)

Podívejme se na několik jednoduchých příkladů.

Čárkovaná čára s délkami úseček deset jednotek (při standardním měřítku pixelů):

dc.SetDash(10)
dc.DrawLine(32, 40, 288, 40)
dc.Stroke()

Má stejný význam – délka čárek je stejná jako šířka mezer:

dc.SetDash(10, 10)
dc.DrawLine(32, 60, 288, 60)
dc.Stroke()

Čerchovaná úsečka – čárky mají délku deseti jednotek, mezery pět jednotek a tečka je dlouhá dvě jednotky:

dc.SetDash(10, 5, 2, 5)
dc.DrawLine(32, 220, 288, 220)
dc.Stroke()

Opět si pochopitelně ukážeme příklad, který vykreslí tento obrázek:

Změna stylu vykreslování stop.

Obrázek 12: Změna stylu vykreslování stop.

Autor: tisnik, podle licence: Rights Managed

Zdrojový kód tohoto demonstračního příkladu vypadá takto:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 12:
//    Balíček gogpu/gg.
//    Nastavení stylu vykreslovaných úseček.
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "12.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        dc.DrawLine(32, 20, 288, 20)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10)
        dc.DrawLine(32, 40, 288, 40)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 10)
        dc.DrawLine(32, 60, 288, 60)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 5)
        dc.DrawLine(32, 80, 288, 80)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 100, 288, 100)
        dc.Stroke()
 
        // šířka vykreslení úsečky
        dc.SetLineWidth(4.0)
        dc.SetLineCap(gg.LineCapButt)
 
        // styl vykreslení úsečky
        dc.DrawLine(32, 140, 288, 140)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10)
        dc.DrawLine(32, 160, 288, 160)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 10)
        dc.DrawLine(32, 180, 288, 180)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 5)
        dc.DrawLine(32, 200, 288, 200)
        dc.Stroke()
 
        // styl vykreslení úsečky
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 220, 288, 220)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Práce s cestami (paths)

V této kapitole se zmíníme i o důležitém konceptu takzvaných cest (paths), protože z cest jsou složeny prakticky všechny složitěji tvarované dvourozměrné objekty, ať již se jedná o objekty otevřené či naopak o objekty uzavřené. Každá cesta se skládá z prakticky libovolného množství takzvaných segmentů (segments), přičemž je zajímavé, že jednotlivé segmenty na sebe mohou, ale také nemusí navazovat (cesta tedy může v případě potřeby obsahovat i „skoky“). Použití cest v 2D grafice samozřejmě není nic nového; spíš by se dalo říci, že se jedná o dlouhým časem prověřenou technologii použitou například v PostScriptu či ve vektorovém formátu SVG (PostScript lze přitom chápat jako souborový formát, grafický metaformát, programovací jazyk a současně i vykreslovací či možná lépe řečeno renderovací knihovnu).

Datový typ gg.Context programátorům nabízí poměrně velké množství příkazů určených pro definici segmentů cest. Jednotlivé typy segmentů budou podrobněji popsány v navazujících kapitolách, proto si zde pouze uveďme jednotlivé metody a jejich význam:

# Metoda Stručný popis
1 ClosePath uzavření cesty nakreslením úsečky do jejího počátečního vrcholu
2 MoveTo přesun aktivního bodu bez kreslení
3 LineTo lineární segment (úsečka), aktivní bod se přesune na konec úsečky
4 QuadraticTo kvadratická Bézierova křivka (jeden segment), aktivní bod se přesune na konec křivky
5 CubicTo kubická Bézierova křivka (jeden segment), aktivní bod se opět přesune na konec křivky

Hlavičky těchto metod lze pochopitelně najít v nápovědě:

func (c *Context) ClosePath()
    ClosePath closes the current subpath.
 
func (c *Context) MoveTo(x, y float64)
    MoveTo starts a new subpath at the given point.
 
func (c *Context) LineTo(x, y float64)
    LineTo adds a line to the current path.
 
func (c *Context) QuadraticTo(cx, cy, x, y float64)
    QuadraticTo adds a quadratic Bezier curve to the current path.
 
func (c *Context) CubicTo(c1x, c1y, c2x, c2y, x, y float64)
    CubicTo adds a cubic Bezier curve to the current path.

Ukažme si nyní relativně jednoduchý demonstrační příklad, který po svém spuštění vykreslí domek jedním tahem. Pro tento účel nám poslouží metody nazvané MoveTo (přesun do počátečního bodu kreslení) a LineTo (kreslení úsečkového segmentu):

Cesta složená z lineárních segmentů (úseček).

Obrázek 13: Domek vykreslený jedním tahem je složený z liniových segmentů (úseček).

Autor: tisnik, podle licence: Rights Managed

Následuje výpis zdrojového kódu tohoto příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 13:
//    Balíček gogpu/gg.
//    Práce s cestou (path).
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "13.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        // začátek vykreslování cesty
        dc.MoveTo(100, 200)
        dc.LineTo(200, 200)
        dc.LineTo(100, 100)
        dc.LineTo(100, 200)
        dc.LineTo(200, 100)
        dc.LineTo(100, 100)
        dc.LineTo(150, 50)
        dc.LineTo(200, 100)
        dc.LineTo(200, 200)
 
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Otevřené a uzavřené cesty

Předchozí příklad si nepatrně upravíme takovým způsobem, že vykreslíme pouze obrys domku. Celá cesta reprezentující obrys tedy může být zapsána takto:

dc.MoveTo(100, 200)
dc.LineTo(100, 100)
dc.LineTo(150, 50)
dc.LineTo(200, 100)
dc.LineTo(200, 200)
dc.LineTo(100, 200)

S výsledkem:

Explicitní zápis pěti lineárních segmentů.

Obrázek 14: Pět lineárních segmentů (úseček) ve skutečnosti tvoří otevřenou cestu.

Autor: tisnik, podle licence: Rights Managed

Celý příklad:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 14:
//    Balíček gogpu/gg.
//    Otevřená cesta (open path).
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "14.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        // začátek vykreslování cesty
        dc.MoveTo(100, 200)
        dc.LineTo(100, 100)
        dc.LineTo(150, 50)
        dc.LineTo(200, 100)
        dc.LineTo(200, 200)
        dc.LineTo(100, 200)
 
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Ve skutečnosti však můžeme cestu uzavřít korektně pomocí metody ClosePath. Samotná deklarace cesty se tedy změní takto:

dc.MoveTo(100, 200)
dc.LineTo(100, 100)
dc.LineTo(150, 50)
dc.LineTo(200, 100)
dc.LineTo(200, 200)
dc.ClosePath()
Uzavřená cesta.

Obrázek 15: Uzavřená cesta.

Autor: tisnik, podle licence: Rights Managed

Upravená varianta zdrojového kódu příkladu nyní vypadá takto:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 15:
//    Balíček gogpu/gg.
//    Uzavřená cesta (closed path).
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "15.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        // začátek vykreslování cesty
        dc.MoveTo(100, 200)
        dc.LineTo(100, 100)
        dc.LineTo(150, 50)
        dc.LineTo(200, 100)
        dc.LineTo(200, 200)
        dc.ClosePath()
 
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Složitější cesty založené na kvadratických a kubických Bézierových křivkách

Při tvorbě cest jsou podporovány jak kvadratické, tak i kubické Bézierovy křivky, což je jen dobře, protože kvadratické křivky jsou často používány například při definici fontů zatímco křivky kubické najdeme například v mnoha vektorových grafických editorech a tím pádem i v souborech exportovaných z těchto nástrojů. Bézierovy kvadratické křivky jsou určeny pouze jedním řídicím bodem a dvojicí bodů kotvicích (koncových). Křivka vždy prochází prvním a třetím bodem (kotvicí body), druhý bod (řídicí) určuje současně oba tečné vektory. Ukázka Bézierovy kvadratické křivky spolu s jejími určujícími body je zobrazena na následujícím obrázku:

Bézierovy kvadratické křivky.

Obrázek 16: Bézierovy kvadratické křivky.

Autor: tisnik, podle licence: Rights Managed

Následuje příklad cesty vytvořené jedinou Bézierovou kvadratickou křivkou. Povšimněte si nutnosti použít metody MoveTo pro specifikaci počátečního bodu křivky, protože v metodě QuadraticTo se uvádí pouze dvojice bodů:

dc.MoveTo(10, 150)
dc.QuadraticTo(50, 10, 90, 150)
dc.Stroke()

Hladké napojení Bézierových kvadratických křivek lze zajistit zadáním identického koncového bodu první křivky a počátečního bodu křivky druhé. Současně musí být shodné tečné vektory v těchto bodech, tj. prostřední (řídicí) body musí být středově symetrické okolo společného bodu obou křivek.

Kubické Bézierovy křivky většina uživatelů používajících vektorové grafické editory již pravděpodobně velmi dobře zná (ale nalezneme je i v rastrových grafických editorech). Připomeňme si tedy, že tyto křivky jsou definovány počátečním bodem, koncovým bodem a dvojicí řídicích bodů (těmi křivka sice obecně neprochází, tyto body však ovlivňují její tvar). Větší množství řídicích bodů dává uživatelům i větší možnosti tvarování křivky, protože je možné vytvořit i esíčko, smyčku atd.

Ve knihovně GG se tyto křivky (resp. přesněji řečeno segmenty složené z kubických Bézierových křivek) přidávají do cesty s využitím metody CubicTo, přičemž tato metoda očekává tři body (přesněji šestici souřadnic), protože počáteční bod již známe – je jím dočasný poslední bod aktuálně vytvářené cesty.

Vykreslení kvadratických Bézierových křivek

Další demonstrační příklad, který si dnes ukážeme, vykreslí trojici Bézierových kvadratických křivek:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 16:
//    Balíček gogpu/gg.
//    Kvadratická Bézierova křivka.
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "16.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        // cesta
        dc.SetRGBA(1.0, 0.0, 0.0, 1.0)
        dc.MoveTo(10, 150)
        dc.QuadraticTo(50, 10, 90, 150)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // cesta
        dc.SetRGBA(0.0, 1.0, 0.0, 1.0)
        dc.MoveTo(110, 100)
        dc.QuadraticTo(190, 100, 150, 190)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // cesta
        dc.SetRGBA(0.0, 0.0, 1.0, 1.0)
        dc.MoveTo(250, 150)
        dc.QuadraticTo(210, 60, 290, 150)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Předchozí příklad je možné snadno upravit takovým způsobem, aby se vykreslila nejenom samotná Bézierova křivka, ale současně se zobrazily i její řídicí body a tečné vektory:

Řídicí body Bézierových kvadratických křivek.

Obrázek 17: Bézierovy kvadratické křivky i se svými řídicími body.

Autor: tisnik, podle licence: Rights Managed

Toto vylepšení je implementováno ve funkci drawQuadraticBezier, kterou nalezneme v dalším příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 17:
//    Balíček gogpu/gg.
//    Kvadratická Bézierova křivka s řídicími body.
 
package main
 
import "github.com/gogpu/gg"
 
func drawQuadraticBezier(dc *gg.Context, x0 float64, y0 float64, x1 float64, y1 float64, x2 float64, y2 float64) {
        dc.SetRGBA(1.0, 0.5, 0.5, 1.0)
        dc.DrawLine(x0, y0, x1, y1)
        dc.Stroke()
        dc.DrawLine(x1, y1, x2, y2)
        dc.Stroke()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1.0)
        dc.MoveTo(x0, y0)
        dc.QuadraticTo(x1, y1, x2, y2)
        dc.Stroke()
 
        dc.SetRGBA(0.2, 0.2, 1.0, 1.0)
 
        dc.DrawCircle(x0, y0, 3)
        // vykreslení
        dc.Stroke()
 
        dc.DrawCircle(x1, y1, 3)
        // vykreslení
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        // vykreslení
        dc.Stroke()
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "17.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        // vykreslení cest
        drawQuadraticBezier(dc, 10, 150, 50, 10, 90, 150)
        drawQuadraticBezier(dc, 110, 100, 190, 100, 150, 190)
        drawQuadraticBezier(dc, 250, 150, 210, 60, 290, 150)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Vykreslení kubických Bézierových křivek

Kubickou Bézierovu křivku (pokud nebude navazovat na další segmenty) lze vykreslit snadno. Nejdříve se samostatně zadá první kotvicí bod a následně trojice bodů (jeden kotvicí a dva řídicí):

// cesta
dc.SetRGBA(1.0, 0.0, 0.0, 1.0)
dc.MoveTo(10, 180)
dc.CubicTo(10, 10, 120, 180, 120, 10)
// vlastní vykreslení cesty
dc.Stroke()

V dalším demonstračním příkladu se vykreslí trojice Bézierových křivek:

Bézierovy kubické křivky.

Obrázek 18: Bézierovy kubické křivky.

Autor: tisnik, podle licence: Rights Managed

Zdrojový kód takto upraveného příkladu se příliš neliší od příkladů z předchozí kapitoly:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 18:
//    Balíček gogpu/gg.
//    Kubická Bézierova křivka.
 
package main
 
import "github.com/gogpu/gg"
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "18.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        // pozadí obrázku
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        // cesta
        dc.SetRGBA(1.0, 0.0, 0.0, 1.0)
        dc.MoveTo(10, 180)
        dc.CubicTo(10, 10, 120, 180, 120, 10)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // cesta
        dc.SetRGBA(0.0, 1.0, 0.0, 1.0)
        dc.MoveTo(110, 180)
        dc.CubicTo(190, 100, 80, 100, 160, 180)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // cesta
        dc.SetRGBA(0.0, 0.0, 1.0, 1.0)
        dc.MoveTo(230, 180)
        dc.CubicTo(280, 60, 230, 60, 280, 180)
        // vlastní vykreslení cesty
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Na závěr této části si ukažme, jak se provede úprava příkladu tak, aby se vykreslily řídicí body i tečné vektory kubických Bézierových křivek:

Řídicí body Bézierových kubických křivek.

Obrázek 19: Řídicí body a tečné vektory Bézierových kubických křivek.

Autor: tisnik, podle licence: Rights Managed

Rotace Bézierovy křivky.

Obrázek 20: Řídicí body a tečné vektory Bézierovy kubické křivky.

Autor: tisnik, podle licence: Rights Managed

Zdrojový kód demonstračního příkladu se v tomto případě změní následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 19:
//    Balíček gogpu/gg.
//    Kubická Bézierova křivka s řídicími body.
 
package main
 
import "github.com/gogpu/gg"
 
func drawCubicBezier(dc *gg.Context,
        x0 float64, y0 float64, x1 float64, y1 float64,
        x2 float64, y2 float64, x3 float64, y3 float64) {
 
        dc.SetRGBA(1.0, 0.5, 0.5, 1.0)
        dc.DrawLine(x0, y0, x1, y1)
        dc.Stroke()
        dc.DrawLine(x1, y1, x2, y2)
        dc.Stroke()
        dc.DrawLine(x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1.0)
        dc.MoveTo(x0, y0)
        dc.CubicTo(x1, y1, x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.2, 0.2, 1.0, 1.0)
 
        dc.DrawCircle(x0, y0, 3)
        dc.Stroke()
 
        dc.DrawCircle(x1, y1, 3)
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        // vykreslení
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        // vykreslení
        dc.Stroke()
 
        dc.DrawCircle(x3, y3, 3)
        // vykreslení
        dc.Stroke()
}
 
func clearCanvas(dc *gg.Context, width float64, height float64) {
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName1 = "19_1.png"
        const fileName2 = "19_2.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
        // vymazání obrázku
        clearCanvas(dc, width, height)
 
        // vykreslení cest
        drawCubicBezier(dc, 10, 180, 10, 10, 120, 180, 120, 10)
        drawCubicBezier(dc, 230, 180, 280, 60, 230, 60, 280, 180)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName1)
 
        // vymazání obrázku
        clearCanvas(dc, width, height)
 
        // vykreslení cesty
        drawCubicBezier(dc, 130, 180, 210, 100, 100, 100, 180, 180)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName2)
}

Rotace Bézierových křivek s využitím transformace zadané metodou RotateAbout

Řídicí body Bézierovy křivky (ale i všechny další vykreslované tvary) je možné podrobit afinní transformaci (posun, rotace, změna měřítka). Výsledkem aplikace transformace pouze na řídicí body bude po vykreslení zcela korektně transformovaná Bézierova křivka, což je samozřejmě výhodné, protože je rychlejší transformovat tři nebo čtyři body a nikoli všechny body, z nichž se skládají segmenty křivky:

func (c *Context) RotateAbout(angle, x, y float64)
    RotateAbout rotates around a specific point.

Jednotlivé transformace se skládají (což si ukážeme v dalším textu), takže v případě, že má být realizováno otočení o absolutní úhel, musí se před natočením uložit poslední stav grafického kontextu na zásobník a po provedení transformace a vykreslení opět tento stav obnovit. K těmto operacím se používají metody Push a Pop. Povšimněte si, že se neukládá pouze aktuálně vypočtená transformace, ale i další stav vykreslovacího řetězce:

func (c *Context) Push()
    Push saves the current state (transform, paint, clip, and mask).
 
func (c *Context) Pop()
    Pop restores the last saved state.

V příkladu je navíc použita i pomocná funkce pro převod úhlů na radiány:

func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}

Další demonstrační příklad po svém spuštění vytvoří několik snímků s různě natočenou Bézierovou kubikou. Význam metody RotateAbout je vysvětlen v navazujícím textu:

Rotace Bézierovy křivky.

Obrázek 21: Rotace Bézierovy křivky, jejích koncových bodů, obalové křivky atd.

Autor: tisnik, podle licence: Rights Managed

Rotace Bézierovy křivky.

Obrázek 22: Rotace Bézierovy křivky, jejích koncových bodů, obalové křivky atd.

Autor: tisnik, podle licence: Rights Managed

Rotace Bézierovy křivky.

Obrázek 23: Rotace Bézierovy křivky, jejích koncových bodů, obalové křivky atd.

Autor: tisnik, podle licence: Rights Managed

Rotace Bézierovy křivky.

Obrázek 24: Rotace Bézierovy křivky, jejích koncových bodů, obalové křivky atd.

Autor: tisnik, podle licence: Rights Managed

Výše zobrazené Bézierovy křivky byly vykresleny tímto příkladem:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 20:
//    Balíček gogpu/gg.
//    Rotace vykreslovaných objektů.
 
package main
 
import (
        "fmt"
        "math"
 
        "github.com/gogpu/gg"
)
 
func drawCubicBezier(dc *gg.Context,
        x0 float64, y0 float64, x1 float64, y1 float64,
        x2 float64, y2 float64, x3 float64, y3 float64) {
 
        dc.SetRGBA(1.0, 0.5, 0.5, 1.0)
        dc.DrawLine(x0, y0, x1, y1)
        dc.Stroke()
        dc.DrawLine(x1, y1, x2, y2)
        dc.Stroke()
        dc.DrawLine(x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1.0)
        dc.MoveTo(x0, y0)
        dc.CubicTo(x1, y1, x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.2, 0.2, 1.0, 1.0)
        dc.DrawCircle(x0, y0, 3)
        dc.Stroke()
 
        dc.DrawCircle(x1, y1, 3)
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        dc.Stroke()
 
        dc.DrawCircle(x3, y3, 3)
        dc.Stroke()
}
 
func clearCanvas(dc *gg.Context, width float64, height float64) {
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
}
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        for alpha := 0; alpha < 360; alpha += 30 {
                clearCanvas(dc, width, height)
                // transformace a vykreslení
                dc.Push()
                dc.RotateAbout(Radians(float64(alpha)), width/2, height/2)
                drawCubicBezier(dc, 130, 180, 210, 100, 100, 100, 180, 180)
                dc.Pop()
                filename := fmt.Sprintf("20_%03d.png", alpha)
                dc.SavePNG(filename)
        }
 
}

Převod sekvence snímků na animaci ve formátu GIF

V této kapitole si vysvětlíme, jakým způsobem vznikla následující jednoduchá animace:

Rotace Bézierovy křivky.

Obrázek 25: Rotace Bézierovy křivky.

Autor: tisnik, podle licence: Rights Managed

Využijeme zde kombinaci technik implementovaných ve standardních knihovnách image/draw a image/gif s knihovnou gogpu/gg.

V animaci se používá rotace okolo zadaného bodu. Používáme zde metody Push a Pop pro uložení a pro následné obnovení tvaru transformační matice:

for alpha := 0; alpha < 360; alpha += 5 {
        clearCanvas(dc, width, height)
        dc.Push()
        dc.RotateAbout(Radians(float64(alpha)), width/2, height/2)
        ...
        vykreslení
        ...
        dc.Pop()
}

Po vykreslení získáme z kontextu knihovny GG vlastní rastrový obrázek, převedeme ho na obrázek s barvovou paletou (pomocí nám již známé funkce Draw ze standardní knihovny image/draw) a uložíme ho do pole obrázků:

package draw // import "image/draw"
 
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
    Draw calls DrawMask with a nil mask.

Současně inicializujeme i pole s časy přechodu mezi jednotlivými snímky:

sourceImage := dc.Image()
palettedImage := image.NewPaletted(image.Rect(0, 0, width, height), palette.Plan9)
draw.Draw(palettedImage, palettedImage.Rect, sourceImage, image.ZP, draw.Over)
images = append(images, palettedImage)
delays = append(delays, 10)
Poznámka: v našem demonstračním příkladu se ve skutečnosti nepracuje přímo s poli, ale s řezy (slice), které ovšem interně pole používají.

A nakonec je nutné všechny snímky vyexportovat do formátu GIF, a to konkrétně pomocí funkce EncodeAll ze standardní knihovny image/gif:

package gif // import "image/gif"
 
func EncodeAll(w io.Writer, g *GIF) error
    EncodeAll writes the images in g to w in GIF format with the given loop
    count and delay between frames.

Realizace je uložení celé animace snadná:

// uložení sekvence obrázků
outfile, err := os.Create(fileName)
if err != nil {
        panic(err)
}
defer outfile.Close()
 
gif.EncodeAll(outfile, &gif.GIF{
        Image: images,
        Delay: delays,
})

Následuje výpis celého zdrojového kódu tohoto demonstračního příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 21:
//    Balíček gogpu/gg.
//    Rasterizace do zvoleného rastrového formátu.
//    Výsledkem bude animovaný GIF.
 
package main
 
import (
        "fmt"
        "image"
        "image/color/palette"
        "image/draw"
        "image/gif"
        "math"
        "os"
 
        "github.com/gogpu/gg"
)
 
func drawCubicBezier(dc *gg.Context,
        x0 float64, y0 float64, x1 float64, y1 float64,
        x2 float64, y2 float64, x3 float64, y3 float64) {
 
        dc.SetRGBA(1.0, 0.5, 0.5, 1.0)
        dc.DrawLine(x0, y0, x1, y1)
        dc.Stroke()
        dc.DrawLine(x1, y1, x2, y2)
        dc.Stroke()
        dc.DrawLine(x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1.0)
        dc.MoveTo(x0, y0)
        dc.CubicTo(x1, y1, x2, y2, x3, y3)
        dc.Stroke()
 
        dc.SetRGBA(0.2, 0.2, 1.0, 1.0)
        dc.DrawCircle(x0, y0, 3)
        dc.Stroke()
 
        dc.DrawCircle(x1, y1, 3)
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        dc.Stroke()
 
        dc.DrawCircle(x2, y2, 3)
        dc.Stroke()
 
        dc.DrawCircle(x3, y3, 3)
        dc.Stroke()
}
 
func clearCanvas(dc *gg.Context, width float64, height float64) {
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
}
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "21.gif"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        var images []*image.Paletted
        var delays []int
 
        for alpha := 0; alpha < 360; alpha += 5 {
                clearCanvas(dc, width, height)
                dc.Push()
                dc.RotateAbout(Radians(float64(alpha)), width/2, height/2)
                drawCubicBezier(dc, 130, 180, 210, 100, 100, 100, 180, 180)
                dc.Pop()
                sourceImage := dc.Image()
                palettedImage := image.NewPaletted(image.Rect(0, 0, width, height), palette.Plan9)
                draw.Draw(palettedImage, palettedImage.Rect, sourceImage, image.ZP, draw.Over)
                images = append(images, palettedImage)
                delays = append(delays, 10)
                fmt.Println(alpha, "of", 360)
        }
 
        // uložení sekvence obrázků
        outfile, err := os.Create(fileName)
        if err != nil {
                panic(err)
        }
        defer outfile.Close()
 
        gif.EncodeAll(outfile, &gif.GIF{
                Image: images,
                Delay: delays,
        })
}

Lineární transformace aplikované při vykreslování 2D tvarů

Všechny vykreslované úsečky a cesty jsou před vlastním renderingem podrobeny afinní transformaci: rotaci, změně měřítka, posunu, popř. libovolné kombinaci transformací (zkosení atd.). První metodou pro zadání transformace je metoda nazvaná Rotate, která provádí rotaci okolo počátku souřadné soustavy (to je důležité):

func (c *Context) Rotate(angle float64)
    Rotate applies a rotation (angle in radians).
Rotace okolo počátku souřadné soustavy.

Obrázek 26: Rotace okolo počátku souřadné soustavy.

Autor: tisnik, podle licence: Rights Managed

V dalším demonstračním příkladu si povšimněte toho, že pro převod radiánů na stupně můžeme použít funkci Radians:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 22:
//    Balíček gogpu/gg.
//    Transformace (rotace).
 
package main
 
import (
        "math"
 
        "github.com/gogpu/gg"
)
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "22.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.Rotate(Radians(15.0))
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        dc.DrawLine(32, 20, 288, 20)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 40, 288, 40)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 60, 288, 60)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 80, 288, 80)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 100, 288, 100)
        dc.Stroke()
 
        dc.SetLineWidth(4.0)
        dc.SetLineCap(gg.LineCapButt)
 
        dc.DrawLine(32, 140, 288, 140)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 160, 288, 160)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 180, 288, 180)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 200, 288, 200)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 220, 288, 220)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Mnohem častější bývá požadavek na rotaci okolo zvoleného bodu, nejenom okolo počátku souřadné soustavy. Zde využijeme metodu pojmenovanou příhodně RotateAbout:

func (c *Context) RotateAbout(angle, x, y float64)
    RotateAbout rotates around a specific point.
Rotace okolo zadaného bodu.

Obrázek 27: Rotace okolo zvoleného bodu.

Autor: tisnik, podle licence: Rights Managed

Příklad, který tuto metodu používá, vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 23:
//    Balíček gogpu/gg.
//    Transformace (rotace okolo zvoleného bodu).
 
package main
 
import (
        "math"
 
        "github.com/gogpu/gg"
)
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "23.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.RotateAbout(Radians(15.0), width/2.0, height/2.0)
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        dc.DrawLine(32, 20, 288, 20)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 40, 288, 40)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 60, 288, 60)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 80, 288, 80)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 100, 288, 100)
        dc.Stroke()
 
        dc.SetLineWidth(4.0)
        dc.SetLineCap(gg.LineCapButt)
 
        dc.DrawLine(32, 140, 288, 140)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 160, 288, 160)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 180, 288, 180)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 200, 288, 200)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 220, 288, 220)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Pro změnu měřítka se používá metoda pojmenovaná Scale (druhá metoda ScaleAbout prozatím chybí). Změnu měřítka většinou potřebujeme vztáhnout k jakémukoli bodu v ploše, nejenom tedy k počátku souřadné soustavy. V takovém případě lze použít skládání transformací – provede se posun do počátku souřadné soustavy, změní se měřítko a následně se provede zpětný posun:

        dc.Translate(width/2.0, height/2.0)
        dc.Scale(2.0, 2.0)
        dc.Translate(-width/2.0, -height/2.0)

Výsledek bude v tomto případě následující:

Změna měřítka.

Obrázek 28: Změna měřítka se projeví i na šířce kreslených stop.

Autor: tisnik, podle licence: Rights Managed

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 24:
//    Balíček gogpu/gg.
//    Transformace (změna měřítka okolo zvoleného bodu).
 
package main
 
import (
        "github.com/gogpu/gg"
)
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "24.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.Translate(width/2.0, height/2.0)
        dc.Scale(2.0, 2.0)
        dc.Translate(-width/2.0, -height/2.0)
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        dc.DrawLine(32, 20, 288, 20)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 40, 288, 40)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 60, 288, 60)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 80, 288, 80)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 100, 288, 100)
        dc.Stroke()
 
        dc.SetLineWidth(4.0)
        dc.SetLineCap(gg.LineCapButt)
 
        dc.DrawLine(32, 140, 288, 140)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 160, 288, 160)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 180, 288, 180)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 200, 288, 200)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 220, 288, 220)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Nic nám samozřejmě nebrání v tom provést složení transformací ručně. Rotace o 15° okolo zadaného bodu [width/2, height/2] se může naprogramovat i tímto způsobem:

        dc.Translate(width/2.0, height/2.0)
        dc.Rotate(Radians(15))
        dc.Translate(-width/2.0, -height/2.0)
Rotace okolo zadaného bodu.

Obrázek 29: Ruční složení několika transformací.

Autor: tisnik, podle licence: Rights Managed

Jen pro doplnění následuje úplný zdrojový kód příkladu:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 25:
//    Balíček gogpu/gg.
//    Transformace (posun).
 
package main
 
import (
        "math"
 
        "github.com/gogpu/gg"
)
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "25.png"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.Translate(width/2.0, height/2.0)
        dc.Rotate(Radians(15))
        dc.Translate(-width/2.0, -height/2.0)
 
        dc.SetRGBA(0.0, 0.0, 0.0, 1)
 
        dc.DrawLine(32, 20, 288, 20)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 40, 288, 40)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 60, 288, 60)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 80, 288, 80)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 100, 288, 100)
        dc.Stroke()
 
        dc.SetLineWidth(4.0)
        dc.SetLineCap(gg.LineCapButt)
 
        dc.DrawLine(32, 140, 288, 140)
        dc.Stroke()
 
        dc.SetDash(10)
        dc.DrawLine(32, 160, 288, 160)
        dc.Stroke()
 
        dc.SetDash(10, 10)
        dc.DrawLine(32, 180, 288, 180)
        dc.Stroke()
 
        dc.SetDash(10, 5)
        dc.DrawLine(32, 200, 288, 200)
        dc.Stroke()
 
        dc.SetDash(10, 5, 2, 5)
        dc.DrawLine(32, 220, 288, 220)
        dc.Stroke()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Práce s fonty, vykreslení textu

Knihovna gogpu/gg podporuje i vykreslování textu. Používají se přitom fonty typu TrueType, které je nejdříve nutné načíst metodou LoadFontFace, jíž se předá jak jméno souboru s fontem, tak i požadovaná velikost písma:

func (c *Context) LoadFontFace(path string, points float64) error
    LoadFontFace loads a font from a file and sets it as the current font.
    The size is specified in points.
 
    Deprecated: Use text.NewFontSourceFromFile and SetFont instead. This method
    is provided for convenience and backward compatibility.
 
    Example (new way):
 
        source, err := text.NewFontSourceFromFile("font.ttf")
        if err != nil {
            return err
        }
        face := source.Face(12.0)
        ctx.SetFont(face)
Poznámka: nápověda má pravdu – měla by se použít knihovna gogpu/gg/text a v ní definovaná funkce NewFontSourceFromFile:
func NewFontSourceFromFile(path string, opts ...SourceOption) (*FontSource, error)
    NewFontSourceFromFile loads a FontSource from a font file path.

Pro vykreslení lze použít metodu DrawString, které se předává souřadnice levého dolního rohu pomyslného obdélníku, do něhož se text vykreslí:

func (c *Context) DrawString(s string, x, y float64)
    DrawString draws text at position (x, y) where y is the baseline. If no font
    has been set with SetFont, this function does nothing.
 
    If a GPU accelerator is registered and supports text rendering (implements
    GPUTextAccelerator), the text is rendered via the GPU MSDF pipeline. The
    CTM (Current Transform Matrix) is passed to the GPU so that Scale, Rotate,
    and Skew transforms affect text rendering, not just position. Otherwise,
    the CPU text pipeline is used with transform-aware rendering:
      - Translation-only: bitmap fast path (zero quality loss)
      - Uniform scale ≤256px: bitmap at device size (Strategy A, Skia pattern)
      - Everything else: glyph outlines as vector paths (Strategy B, Vello
        pattern)
 
    The baseline is the line on which most letters sit. Characters with
    descenders (like 'g', 'j', 'p', 'q', 'y') extend below the baseline.
Text vykreslený do počátku souřadné soustavy.

Obrázek 30: Vykreslení textu do počátku souřadné soustavy.

Autor: tisnik, podle licence: Rights Managed

Podívejme se na příklad, který po svém spuštění vytvoří obrázek s textem „Hello, world!“ (font je uložen do repositáře se všemi demonstračními příklady):

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 26:
//    Balíček gogpu/gg.
//    Vykreslení textu.
 
package main
 
import (
        "fmt"
 
        "github.com/gogpu/gg"
)
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "26.png"
 
        // jméno souboru s fontem
        const fontFileName = "fonts/FreeSans.ttf"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGB(0.0, 0.0, 0.0)
 
        // pokus o načtení fontu
        if err := dc.LoadFontFace(fontFileName, 36); err != nil {
                fmt.Println("Cannot load font")
                panic(err)
        }
        dc.DrawString("Hello, world!", 0, height)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

V případě, že je nutné text vycentrovat, můžeme namísto metody DrawString použít metodu DrawStringAnchored, které se kromě vlastního textu předají i souřadnice vztažného bodu (anchor) a taktéž dvojice vah udávajících relativní pozici textu k tomuto bodu:

func (c *Context) DrawStringAnchored(s string, x, y, ax, ay float64)
    DrawStringAnchored draws text with an anchor point. The anchor point is
    specified by ax and ay, which are in the range [0, 1].
 
        (0, 0) = top-left
        (0.5, 0.5) = center
        (1, 1) = bottom-right
 
    The text is positioned so that the anchor point is at (x, y).

Pro vycentrování použijte hodnoty 0,5:

dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)

S výsledkem:

Text vykreslený doprostřed plochy kreslicího plátna.

Obrázek 31: Vykreslení vycentrovaného textu.

Autor: tisnik, podle licence: Rights Managed

Tento obrázek byl vykreslen následujícím příkladem:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 27:
//    Balíček gogpu/gg.
//    Vykreslení vycentrovaného textu.
 
package main
 
import (
        "fmt"
 
        "github.com/gogpu/gg"
)
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "27.png"
 
        // jméno souboru s fontem
        const fontFileName = "fonts/FreeSans.ttf"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGB(0.0, 0.0, 0.0)
 
        // pokus o načtení fontu
        if err := dc.LoadFontFace(fontFileName, 36); err != nil {
                fmt.Println("Cannot load font")
                panic(err)
        }
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Můžeme si samozřejmě vyzkoušet, jak se projeví různé hodnoty předané do předposledního parametru metody DrawStringAnchored. Tento parametr určuje, kterou část textu má vztažný bod určovat. Parametry budeme měnit od hodnoty –1,0 do 1,0:

var h float64 = 20
weight := -1.0
 
for i := 0; i < 11; i++ {
        dc.DrawStringAnchored("Hello, world!", width/2, h, weight, 0.5)
        h += 20
        weight += 0.2
}

S výsledkem:

Specifikace způsobu výpočtu horizontálního vycentrování textu.

Obrázek 32: Vliv změny polohy vztažného bodu při vykreslování textu.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód tohoto příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 28:
//    Balíček gogpu/gg.
//    Vykreslení vycentrovaného textu.
 
package main
 
import (
        "fmt"
 
        "github.com/gogpu/gg"
)
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "28.png"
 
        // jméno souboru s fontem
        const fontFileName = "fonts/FreeSans.ttf"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGB(0.0, 0.0, 0.0)
 
        // pokus o načtení fontu
        if err := dc.LoadFontFace(fontFileName, 24); err != nil {
                fmt.Println("Cannot load font")
                panic(err)
        }
 
        var h float64 = 20
        weight := -1.0
 
        for i := 0; i < 11; i++ {
                dc.DrawStringAnchored("Hello, world!", width/2, h, weight, 0.5)
                h += 20
                weight += 0.2
        }
 
        dc.SavePNG(fileName)
}

A v dnešním posledním demonstračním příkladu si ukážeme vykreslení textu, který je orotován okolo specifikovaného bodu:

        // transformace a vykreslení
        dc.Push()
        dc.RotateAbout(Radians(0.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.RotateAbout(Radians(120.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.RotateAbout(Radians(120.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.Pop()

Výsledek – trojice textů – bude vypadat následovně:

Školení Zabbix

Rotace textu okolo zadaného bodu.

 Obrázek 33: Rotace textu okolo specifikovaného bodu.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód dnešního posledního demonstračního příkladu vypadá následovně:

// Seriál "Programovací jazyk Go"
//    https://www.root.cz/serialy/programovaci-jazyk-go/
//
// Demonstrační příklad číslo 29:
//    Balíček gogpu/gg.
//    Vykreslení vycentrovaného textu.
 
package main
 
import (
        "fmt"
        "math"
 
        "github.com/gogpu/gg"
)
 
func Radians(angle float64) float64 {
        return float64(angle / 180.0 * math.Pi)
}
 
func main() {
        // rozměry rastrového obrázku
        const width = 320
        const height = 240
 
        // jméno souboru s výsledným obrázkem
        const fileName = "29.png"
 
        // jméno souboru s fontem
        const fontFileName = "fonts/FreeSans.ttf"
 
        // vytvoření kontextu
        dc := gg.NewContext(width, height)
 
        dc.DrawRectangle(0, 0, width, height)
        dc.SetRGB(1.0, 1.0, 1.0)
        dc.Fill()
 
        dc.SetRGB(0.0, 0.0, 0.0)
 
        // pokus o načtení fontu
        if err := dc.LoadFontFace(fontFileName, 36); err != nil {
                fmt.Println("Cannot load font")
                panic(err)
        }
        // transformace a vykreslení
        dc.Push()
        dc.RotateAbout(Radians(0.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.RotateAbout(Radians(120.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.RotateAbout(Radians(120.0), width/2, height/2)
        dc.DrawStringAnchored("Hello, world!", width/2, height/2, 0.5, 0.5)
        dc.Pop()
 
        // uložení výsledného rastrového obrázku
        dc.SavePNG(fileName)
}

Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – relativně malý, dnes má přibližně šest až sedm megabajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 01_clear_screen.go vymazání obsahu rastrového obrázku https://github.com/tisnik/go-root/blob/master/article_AF/01_cle­ar_screen.go
2 02_fill_rectangle.go vymazání obrázku vykreslením vyplněného obdélníku https://github.com/tisnik/go-root/blob/master/article_AF/02_fi­ll_rectangle.go
3 03_clear_screen_color.go vymazání obrazovky (lepší varianta) https://github.com/tisnik/go-root/blob/master/article_AF/03_cle­ar_screen_color.go
4 04_clear_screen_color.go vymazání obrazovky (varianta se specifikací barvy) https://github.com/tisnik/go-root/blob/master/article_AF/04_cle­ar_screen_color.go
5 05_clear_screen_color.go vymazání obrazovky (varianta se specifikací barvy) https://github.com/tisnik/go-root/blob/master/article_AF/05_cle­ar_screen_color.go
6 06_colors.go práce s barvami https://github.com/tisnik/go-root/blob/master/article_AF/06_co­lors.go
7 07_color_alpha.go práce s barvami, alfa složka https://github.com/tisnik/go-root/blob/master/article_AF/07_co­lor_alpha.go
8 08_rgba_color.go barvový prostor RGB a RGBA https://github.com/tisnik/go-root/blob/master/article_AF/08_rgba_co­lor.go
9 09_alpha_component.go barvový prostor RGB a RGBA. Alfa kanál v barvovém prostoru RGBA https://github.com/tisnik/go-root/blob/master/article_AF/09_al­pha_component.go
10 10_line_width.go nastavení šířky vykreslovaných úseček a křivek https://github.com/tisnik/go-root/blob/master/article_AF/10_li­ne_width.go
11 11_line_caps.go nastavení stylu konců úseček https://github.com/tisnik/go-root/blob/master/article_AF/11_li­ne_caps.go
12 12_dash.go nastavení stylu vykreslovaných úseček https://github.com/tisnik/go-root/blob/master/article_AF/12_dash.go
13 13_simple_path.go práce s cestou (path) https://github.com/tisnik/go-root/blob/master/article_AF/13_sim­ple_path.go
14 14_open_path.go otevřená cesta (open path) https://github.com/tisnik/go-root/blob/master/article_AF/14_o­pen_path.go
15 15_closed_path.go uzavřená cesta (closed path) https://github.com/tisnik/go-root/blob/master/article_AF/15_clo­sed_path.go
16 16_quadratic_bezier.go kvadratická Bézierova křivka https://github.com/tisnik/go-root/blob/master/article_AF/16_qu­adratic_bezier.go
17 17_quadratic_bezier_control_points.go kvadratická Bézierova křivka s řídicími body https://github.com/tisnik/go-root/blob/master/article_AF/17_qu­adratic_bezier_control_po­ints.go
18 18_cubic_bezier.go kubická Bézierova křivka https://github.com/tisnik/go-root/blob/master/article_AF/18_cu­bic_bezier.go
19 19_cubic_bezier_control_points.go kubická Bézierova křivka s řídicími body https://github.com/tisnik/go-root/blob/master/article_AF/19_cu­bic_bezier_control_points­.go
20 20_rotation.go rotace vykreslovaných objektů https://github.com/tisnik/go-root/blob/master/article_AF/20_ro­tation.go
21 21_to_image.go rasterizace do zvoleného rastrového formátu. Výsledkem bude animovaný GIF https://github.com/tisnik/go-root/blob/master/article_AF/21_to_i­mage.go
22 22_transformation_rotate.go transformace (rotace) https://github.com/tisnik/go-root/blob/master/article_AF/22_tran­sformation_rotate.go
23 23_transformation_rotate_about.go transformace (rotace okolo zvoleného bodu) https://github.com/tisnik/go-root/blob/master/article_AF/23_tran­sformation_rotate_about.go
24 24_scale_about.go transformace (změna měřítka okolo zvoleného bodu) https://github.com/tisnik/go-root/blob/master/article_AF/24_sca­le_about.go
25 25_translate_rotate.go transformace (posun) https://github.com/tisnik/go-root/blob/master/article_AF/25_tran­slate_rotate.go
26 26_text.go vykreslení textu https://github.com/tisnik/go-root/blob/master/article_AF/26_text.go
27 27_text_centered.go vykreslení vycentrovaného textu https://github.com/tisnik/go-root/blob/master/article_AF/27_tex­t_centered.go
28 28_text_centered.go vykreslení vycentrovaného textu https://github.com/tisnik/go-root/blob/master/article_AF/28_tex­t_centered.go
29 29_text_rotated.go vykreslení vycentrovaného textu, který je rotován https://github.com/tisnik/go-root/blob/master/article_AF/29_tex­t_rotated.go
30 go.mod   https://github.com/tisnik/go-root/blob/master/article_AF/go.mod

Odkazy na Internetu

  1. Projekt GoGPU na GitHubu
    https://github.com/orgs/gog­pu/repositories
  2. Balíček gogpu/gg
    https://github.com/gogpu/gg
  3. Balíček gogpu/gg-svg
    https://github.com/gogpu/gg-svg
  4. Balíček gogpu/gg-pdf
    https://github.com/gogpu/gg-pdf
  5. package gg (což je jiná varianta gg)
    https://godoc.org/github.com/fo­gleman/gg
  6. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  7. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  8. The Go image package
    https://blog.golang.org/go-image-package
  9. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  10. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  11. Immediate mode (computer graphics)
    https://en.wikipedia.org/wi­ki/Immediate_mode_(computer_grap­hics)
  12. Retained mode
    https://en.wikipedia.org/wi­ki/Retained_mode
  13. Lecture 11: Linear Interpolation Again – Bézier Curves
    http://www.math.kent.edu/~re­ichel/courses/intr.num.com­p.1/fall09/lecture12/bez.pdf
  14. Geometrie/Úvod do křivek
    https://cs.wikibooks.org/wi­ki/Geometrie/%C3%9Avod_do_k%C5%99i­vek
  15. Seriál Grafická knihovna OpenGL
    https://www.root.cz/serialy/graficka-knihovna-opengl/
  16. Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
    https://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/
  17. Seriál OpenGL a nadstavbová knihovna GLU
    https://www.root.cz/serialy/opengl-a-nadstavbova-knihovna-glu/
  18. Seriál OpenGL evaluátory
    https://www.root.cz/serialy/opengl-evaluatory/
  19. Khronos Group
    https://www.khronos.org/
  20. Khronos Group (Wikipedia)
    https://en.wikipedia.org/wi­ki/Khronos_Group
  21. OpenVG (Wikipedia)
    https://en.wikipedia.org/wiki/OpenVG
  22. OpenVG Quick Reference Card
    https://www.khronos.org/files/openvg-quick-reference-card.pdf
  23. OpenVG on the Raspberry Pi
    http://mindchunk.blogspot­.cz/2012/09/openvg-on-raspberry-pi.html
  24. ShivaVG: open-source ANSI C OpenVG
    http://ivanleben.blogspot­.cz/2007/07/shivavg-open-source-ansi-c-openvg.html
  25. Testbed for exploring OpenVG on the Raspberry Pi
    https://github.com/ajstarks/openvg
  26. Seriál Grafické karty a grafické akcelerátory
    http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/
  27. (Linux) Framebuffer
    http://wiki.linuxquestion­s.org/wiki/Framebuffer
  28. (Linux) Framebuffer HOWTO
    http://tldp.org/HOWTO/Framebuffer-HOWTO/
  29. Linux framebuffer (Wikipedia)
    https://en.wikipedia.org/wi­ki/Linux_framebuffer
  30. Seriál Grafické formáty
    http://www.root.cz/serialy/graficke-formaty/
  31. Seriál Grafický formát GIF
    https://www.root.cz/serialy/graficky-format-gif/
  32. PNG is Not GIF
    https://www.root.cz/clanky/png-is-not-gif/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.