16 changed files with 356 additions and 14 deletions
@ -0,0 +1,21 @@ |
|||
# The MIT License (MIT) |
|||
|
|||
Copyright (c) 2020 Noah Petherbridge |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
@ -1,7 +1,144 @@ |
|||
# Render: Go Graphics Library |
|||
|
|||
Render is a graphics library written in Go which targets desktop applications on |
|||
Windows, MacOS and Linux as well as WebAssembly to run in the browser. |
|||
Render is a graphics rendering library for Go. |
|||
|
|||
For desktop systems it uses SDL2 under the hood, and in WebAssembly it interacts |
|||
with an HTML Canvas element for drawing. |
|||
It supports SDL2 and HTML Canvas back-ends enabling its use for both desktop |
|||
applications (Linux, Mac and Windows) and WebAssembly modules for running in |
|||
the web browser. |
|||
|
|||
 |
|||
|
|||
## Example |
|||
|
|||
```go |
|||
package main |
|||
|
|||
import ( |
|||
"git.kirsle.net/go/render" |
|||
"git.kirsle.net/go/render/sdl" |
|||
) |
|||
|
|||
func main() { |
|||
mw := sdl.New("Hello World", 320, 240) |
|||
|
|||
if err := mw.Setup(); err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
// Text that we're gonna draw in the window. |
|||
text := render.Text{ |
|||
Text: "Hello, world!", |
|||
Size: 24, |
|||
Color: render.SkyBlue, |
|||
Shadow: render.Blue, |
|||
FontFilename: "DejaVuSans.ttf", |
|||
} |
|||
|
|||
// Compute the rendered size of the text. |
|||
rect, _ := mw.ComputeTextRect(text) |
|||
|
|||
for { |
|||
// Blank the window. |
|||
mw.Clear(render.White) |
|||
|
|||
// Poll for events (mouse clicks, keyboard keys, etc.) |
|||
ev, err := mw.Poll() |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
// Escape key closes the window. |
|||
if ev.Escape { |
|||
mw.Teardown() |
|||
break |
|||
} |
|||
|
|||
// Get the window size. |
|||
w, h := mw.WindowSize() |
|||
|
|||
// Draw the text centered in the window. |
|||
mw.DrawText(text, render.NewPoint( |
|||
(int32(w)/2)-(rect.W/2), |
|||
(int32(h)/2)-(rect.H/2), |
|||
)) |
|||
|
|||
mw.Present() |
|||
} |
|||
} |
|||
``` |
|||
|
|||
See the `examples/` directory for examples. More will come eventually, |
|||
including some WebAssembly examples. |
|||
|
|||
## Project Status: Alpha |
|||
|
|||
This module was written as part of my drawing-based maze game, code named |
|||
[Project: Doodle](https://www.kirsle.net/doodle). It is currently in |
|||
**alpha status** and its API may change and be cleaned up in the future. |
|||
|
|||
There are a few API cleanup tasks on my to-do list for this project, but they |
|||
will come later when I have a chance to update the Project: Doodle game |
|||
accordingly: |
|||
|
|||
* I want to replace all `int32` types with normal `int` -- int32 was used |
|||
initially due to SDL2 but for the Go API I want to abstract this away. |
|||
|
|||
## Drawing Methods (Engine) |
|||
|
|||
This package provides some _basic_ primitive drawing methods which are |
|||
implemented for SDL2 (desktops) and HTML Canvas (WebAssembly). See the |
|||
render.Engine interface. The drawing methods supported are: |
|||
|
|||
* Clear(Color): blank the window and fill it with this color. |
|||
* DrawPoint(Color, Point): draw a single pixel at a coordinate. |
|||
* DrawLine(Color, A Point, B Point): draw a line between two points. |
|||
* DrawRect(Color, Rect): draw a rectangle outline between two points. |
|||
* DrawBox(Color, Rect): draw a filled rectangle between two points. |
|||
* DrawText(Text, Point): draw text at a location. |
|||
* StoreTexture(name string, image.Image): load a Go image.Image object into |
|||
the engine as a "texture" that can be re-used and pasted on the canvas. |
|||
* LoadTexture(filename string): load an image from disk into a texture. |
|||
* Copy(Texturer, src Rect, dst Rect): copy a texture onto the canvas. |
|||
|
|||
## Drawing Types |
|||
|
|||
This package defines a handful of types useful for drawing operations. |
|||
See the godoc for full details. |
|||
|
|||
**Note:** all int32 values are to become normal ints at some point in the |
|||
future, pending refactor in my Project: Doodle game. |
|||
|
|||
* Color: an RGBA color holding uint8 values for each channel. |
|||
* NewRGBA(red, green, blue, alpha uint8) to construct a new color. |
|||
* Point: holds an X,Y pair of coordinates (int32, to become int at some point) |
|||
* Rect: holds an X,Y and a W,H value (int32). |
|||
* Text: holds text and configuration for rendering (color, stroke, shadow, |
|||
size, etc.) |
|||
|
|||
## Shape Generator Functions |
|||
|
|||
The render package includes a few convenience functions for drawing |
|||
complex shapes. |
|||
|
|||
The generator functions return a channel that yields all of the Points |
|||
that should be drawn to complete the shape. Example: |
|||
|
|||
```go |
|||
var ( |
|||
A Point = render.NewPoint(10, 10) |
|||
B Point = render.NewPoint(15, 20) |
|||
) |
|||
|
|||
for pt := range render.IterLine(A, B) { |
|||
engine.DrawPoint(render.Red, pt) |
|||
} |
|||
``` |
|||
|
|||
* IterLine(A Point, B Point): draw a line from A to B. |
|||
* IterRect(A Point, B Point): iterate all the points to draw a rectangle. |
|||
* IterEllipse(A Point, B Point): draw an elipse fitting inside the |
|||
rectangle bounded by points A and B. |
|||
|
|||
## License |
|||
|
|||
MIT. |
|||
|
Binary file not shown.
@ -0,0 +1,20 @@ |
|||
# Hello World Example |
|||
|
|||
 |
|||
|
|||
This example program draws a yellow window, with a red "ridged" border |
|||
and a blue Gopher that bounces around inside the bordered area. Some blue |
|||
text with drop-shadow is also present. |
|||
|
|||
The window can be resized and the gopher will find his way into the visible |
|||
area if he ends up outside of it. |
|||
|
|||
To run this example: `go run main.go` |
|||
|
|||
## Credits |
|||
|
|||
Gopher image was created by Takuya Ueda (https://twitter.com/tenntenn) |
|||
from https://github.com/golang-samples/gopher-vector |
|||
|
|||
This example comes with the DejaVu Sans font. License information |
|||
at https://dejavu-fonts.github.io/License.html |
After Width: | Height: | Size: 7.3 KiB |
@ -0,0 +1,164 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"image/png" |
|||
"log" |
|||
"os" |
|||
"time" |
|||
|
|||
"git.kirsle.net/go/render" |
|||
"git.kirsle.net/go/render/sdl" |
|||
) |
|||
|
|||
var ( |
|||
// Cap animation speed to 60 FPS
|
|||
targetFPS = 1000 / 60 |
|||
|
|||
// Gopher sprite variables
|
|||
gopher render.Texturer |
|||
texSize render.Rect |
|||
speed int32 = 4 |
|||
position = render.NewPoint(0, 0) |
|||
velocity = render.NewPoint(speed, speed) |
|||
|
|||
// Decorative border variables
|
|||
borderColor = render.Red |
|||
borderSize int32 = 12 |
|||
|
|||
// Background color of the window.
|
|||
bgColor = render.RGBA(255, 255, 128, 255) |
|||
) |
|||
|
|||
func main() { |
|||
mw := sdl.New("Hello World", 800, 600) |
|||
setup(mw) |
|||
|
|||
for { |
|||
start := time.Now() |
|||
|
|||
ev, err := mw.Poll() |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
if ev.Escape { |
|||
mw.Teardown() |
|||
break |
|||
} |
|||
|
|||
update(mw) |
|||
draw(mw) |
|||
mw.Present() |
|||
|
|||
// Delay to maintain constant 60 FPS.
|
|||
var delay uint32 |
|||
elapsed := time.Now().Sub(start) |
|||
tmp := elapsed / time.Millisecond |
|||
if targetFPS-int(tmp) > 0 { |
|||
delay = uint32(targetFPS - int(tmp)) |
|||
} |
|||
mw.Delay(delay) |
|||
} |
|||
} |
|||
|
|||
func setup(e render.Engine) { |
|||
if err := e.Setup(); err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
// Load gopher sprite.
|
|||
fh, err := os.Open("gopher.png") |
|||
if err != nil { |
|||
log.Fatalf("read gopher.png: %s", err) |
|||
} |
|||
img, _ := png.Decode(fh) |
|||
|
|||
gopher, _ = e.StoreTexture("gopher.png", img) |
|||
texSize = gopher.Size() |
|||
} |
|||
|
|||
func update(e render.Engine) { |
|||
position.X += velocity.X |
|||
position.Y += velocity.Y |
|||
|
|||
// Bounce off the walls.
|
|||
w, h := e.WindowSize() |
|||
|
|||
if velocity.X > 0 && position.X+texSize.W >= int32(w)-borderSize { |
|||
velocity.X *= -1 |
|||
} else if velocity.X < 0 && position.X <= borderSize { |
|||
velocity.X *= -1 |
|||
} |
|||
|
|||
if velocity.Y > 0 && position.Y+texSize.H >= int32(h)-borderSize { |
|||
velocity.Y *= -1 |
|||
} else if velocity.Y < 0 && position.Y <= borderSize { |
|||
velocity.Y *= -1 |
|||
} |
|||
} |
|||
|
|||
func draw(e render.Engine) { |
|||
w, h := e.WindowSize() |
|||
|
|||
drawBorder(e, w, h) |
|||
|
|||
// Draw some text centered along the top of the canvas.
|
|||
text := render.Text{ |
|||
Text: "Hello, world!", |
|||
Size: 24, |
|||
Color: render.SkyBlue, |
|||
Shadow: render.Blue, |
|||
FontFilename: "DejaVuSans.ttf", |
|||
} |
|||
rect, _ := e.ComputeTextRect(text) |
|||
e.DrawText(text, render.NewPoint( |
|||
(int32(w)/2)-(rect.W/2), |
|||
25, |
|||
)) |
|||
|
|||
e.Copy(gopher, texSize, render.Rect{ |
|||
X: position.X, |
|||
Y: position.Y, |
|||
W: texSize.W, |
|||
H: texSize.H, |
|||
}) |
|||
} |
|||
|
|||
func drawBorder(e render.Engine, w, h int) { |
|||
// Draw the decorative border. We're going for a "ridged" border
|
|||
// style here. First draw the light and dark edges of the top/left
|
|||
// sides of the border.
|
|||
e.DrawBox(borderColor.Lighten(40), render.Rect{ |
|||
X: 0, |
|||
Y: 0, |
|||
W: int32(w), |
|||
H: int32(h), |
|||
}) |
|||
e.DrawBox(borderColor.Darken(40), render.Rect{ |
|||
X: borderSize / 2, |
|||
Y: borderSize / 2, |
|||
W: int32(w) - (borderSize / 2), |
|||
H: int32(h) - (borderSize / 2), |
|||
}) |
|||
|
|||
// Now inset a bit and draw the light/dark edges of the bottom/right.
|
|||
e.DrawBox(borderColor.Darken(40), render.Rect{ |
|||
X: borderSize, |
|||
Y: borderSize, |
|||
W: int32(w), |
|||
H: int32(h), |
|||
}) |
|||
e.DrawBox(borderColor.Lighten(40), render.Rect{ |
|||
X: borderSize, |
|||
Y: borderSize, |
|||
W: int32(w) - borderSize - (borderSize / 2), |
|||
H: int32(h) - borderSize - (borderSize / 2), |
|||
}) |
|||
|
|||
e.DrawBox(bgColor, render.Rect{ |
|||
X: borderSize, |
|||
Y: borderSize, |
|||
W: int32(w) - (borderSize * 2), |
|||
H: int32(h) - (borderSize * 2), |
|||
}) |
|||
} |
After Width: | Height: | Size: 14 KiB |
Loading…
Reference in new issue