Add image.DrawLine / DrawLines

This commit is contained in:
Hajime Hoshi 2015-01-17 12:45:19 +09:00
parent 22e6996f74
commit 8c15b57d58
12 changed files with 217 additions and 51 deletions

View File

@ -26,8 +26,10 @@ const (
) )
func update(screen *ebiten.Image) error { func update(screen *ebiten.Image) error {
screen.DrawRect(10, 10, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) screen.FillRect(10, 10, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80})
screen.DrawRect(20, 20, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) screen.FillRect(20, 20, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80})
screen.DrawLine(130, 10, 140, 150, color.NRGBA{0xff, 0x80, 0x80, 0x80})
screen.DrawLine(140, 10, 150, 150, color.NRGBA{0xff, 0x80, 0x80, 0x80})
return nil return nil
} }

View File

@ -27,7 +27,7 @@ const (
FilterLinear FilterLinear
) )
func glFilter(c *opengl.Context, filter Filter) opengl.FilterType { func glFilter(c *opengl.Context, filter Filter) opengl.Filter {
switch filter { switch filter {
case FilterNearest: case FilterNearest:
return c.Nearest return c.Nearest

View File

@ -95,15 +95,28 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
return return
} }
// DrawLine draws a line.
func (i *Image) DrawLine(x0, y0, x1, y1 int, clr color.Color) error {
return i.DrawLines(&line{x0, y0, x1, y1, clr})
}
// DrawLines draws lines.
func (i *Image) DrawLines(lines Lines) (err error) {
ui.Use(func(c *opengl.Context) {
err = i.framebuffer.DrawLines(c, lines)
})
return
}
// DrawRect draws a rectangle. // DrawRect draws a rectangle.
func (i *Image) DrawRect(x, y, width, height int, clr color.Color) error { func (i *Image) FillRect(x, y, width, height int, clr color.Color) error {
return i.DrawRects(&rect{x, y, width, height, clr}) return i.FillRects(&rect{x, y, width, height, clr})
} }
// DrawRects draws rectangles on the image. // DrawRects draws rectangles on the image.
func (i *Image) DrawRects(rects Rects) (err error) { func (i *Image) FillRects(rects Rects) (err error) {
ui.Use(func(c *opengl.Context) { ui.Use(func(c *opengl.Context) {
err = i.framebuffer.DrawRects(c, rects) err = i.framebuffer.FillRects(c, rects)
}) })
return return
} }

View File

@ -114,16 +114,30 @@ func (f *Framebuffer) DrawTexture(c *opengl.Context, t *Texture, quads TextureQu
return shader.DrawTexture(c, t.native, p, quads, geo, clr) return shader.DrawTexture(c, t.native, p, quads, geo, clr)
} }
type Lines interface {
Len() int
Points(i int) (x0, y0, x1, y1 int)
Color(i int) color.Color
}
func (f *Framebuffer) DrawLines(c *opengl.Context, lines Lines) error {
if err := f.setAsViewport(c); err != nil {
return err
}
p := f.projectionMatrix()
return shader.DrawLines(c, p, lines)
}
type Rects interface { type Rects interface {
Len() int Len() int
Rect(i int) (x, y, width, height int) Rect(i int) (x, y, width, height int)
Color(i int) color.Color Color(i int) color.Color
} }
func (f *Framebuffer) DrawRects(c *opengl.Context, rects Rects) error { func (f *Framebuffer) FillRects(c *opengl.Context, rects Rects) error {
if err := f.setAsViewport(c); err != nil { if err := f.setAsViewport(c); err != nil {
return err return err
} }
p := f.projectionMatrix() p := f.projectionMatrix()
return shader.DrawRects(c, p, rects) return shader.FillRects(c, p, rects)
} }

View File

@ -85,7 +85,51 @@ func DrawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4
return nil return nil
} }
c.BufferSubData(c.ArrayBuffer, vertices) c.BufferSubData(c.ArrayBuffer, vertices)
c.DrawElements(6 * num) c.DrawElements(c.Triangles, 6*num)
return nil
}
type Lines interface {
Len() int
Points(i int) (x0, y0, x1, y1 int)
Color(i int) color.Color
}
func DrawLines(c *opengl.Context, projectionMatrix *[4][4]float64, lines Lines) error {
if !initialized {
if err := initialize(c); err != nil {
return err
}
initialized = true
}
if lines.Len() == 0 {
return nil
}
f := useProgramLines(c, glMatrix(projectionMatrix))
defer f.FinishProgram()
vertices := vertices[0:0]
num := 0
for i := 0; i < lines.Len(); i++ {
x0, y0, x1, y1 := lines.Points(i)
if x0 == x1 && y0 == y1 {
continue
}
r, g, b, a := lines.Color(i).RGBA()
vertices = append(vertices,
int16(x0), int16(y0), int16(r), int16(g), int16(b), int16(a),
int16(x1), int16(y1), int16(r), int16(g), int16(b), int16(a),
)
num++
}
if len(vertices) == 0 {
return nil
}
c.BufferSubData(c.ArrayBuffer, vertices)
c.DrawElements(c.Lines, 2*num)
return nil return nil
} }
@ -95,7 +139,7 @@ type Rects interface {
Color(i int) color.Color Color(i int) color.Color
} }
func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) error { func FillRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) error {
if !initialized { if !initialized {
if err := initialize(c); err != nil { if err := initialize(c); err != nil {
return err return err
@ -107,7 +151,7 @@ func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects)
return nil return nil
} }
f := useProgramRect(c, glMatrix(projectionMatrix)) f := useProgramRects(c, glMatrix(projectionMatrix))
defer f.FinishProgram() defer f.FinishProgram()
vertices := vertices[0:0] vertices := vertices[0:0]
@ -131,7 +175,7 @@ func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects)
return nil return nil
} }
c.BufferSubData(c.ArrayBuffer, vertices) c.BufferSubData(c.ArrayBuffer, vertices)
c.DrawElements(6 * num) c.DrawElements(c.Triangles, 6*num)
return nil return nil
} }

View File

@ -16,13 +16,20 @@ package shader
import ( import (
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"math"
)
var (
indexBufferLines opengl.Buffer
indexBufferQuads opengl.Buffer
) )
var ( var (
programTexture opengl.Program programTexture opengl.Program
programRect opengl.Program programSolid opengl.Program
) )
// TODO: Use math.MaxUint16??
const quadsMaxNum = 65536 / 6 const quadsMaxNum = 65536 / 6
// unsafe.SizeOf can't be used because unsafe doesn't work with GopherJS. // unsafe.SizeOf can't be used because unsafe doesn't work with GopherJS.
@ -47,11 +54,11 @@ func initialize(c *opengl.Context) error {
} }
defer c.DeleteShader(shaderFragmentTextureNative) defer c.DeleteShader(shaderFragmentTextureNative)
shaderFragmentRectNative, err := c.NewShader(c.FragmentShader, shader(c, shaderFragmentRect)) shaderFragmentSolidNative, err := c.NewShader(c.FragmentShader, shader(c, shaderFragmentSolid))
if err != nil { if err != nil {
return err return err
} }
defer c.DeleteShader(shaderFragmentRectNative) defer c.DeleteShader(shaderFragmentSolidNative)
programTexture, err = c.NewProgram([]opengl.Shader{ programTexture, err = c.NewProgram([]opengl.Shader{
shaderVertexNative, shaderVertexNative,
@ -61,9 +68,9 @@ func initialize(c *opengl.Context) error {
return err return err
} }
programRect, err = c.NewProgram([]opengl.Shader{ programSolid, err = c.NewProgram([]opengl.Shader{
shaderVertexColorNative, shaderVertexColorNative,
shaderFragmentRectNative, shaderFragmentSolidNative,
}) })
if err != nil { if err != nil {
return err return err
@ -81,7 +88,14 @@ func initialize(c *opengl.Context) error {
indices[6*i+4] = 4*i + 2 indices[6*i+4] = 4*i + 2
indices[6*i+5] = 4*i + 3 indices[6*i+5] = 4*i + 3
} }
c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw) indexBufferQuads = c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw)
// TODO: Use math.MaxUint16
indices = make([]uint16, 65536)
for i := uint16(0); i < math.MaxUint16; i++ {
indices[i] = i
}
indexBufferLines = c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw)
return nil return nil
} }
@ -101,6 +115,8 @@ func useProgramTexture(c *opengl.Context, projectionMatrix []float32, texture op
} }
program := programTexture program := programTexture
c.BindElementArrayBuffer(indexBufferQuads)
c.UniformFloats(program, "projection_matrix", projectionMatrix) c.UniformFloats(program, "projection_matrix", projectionMatrix)
ma := float32(geo.Element(0, 0)) ma := float32(geo.Element(0, 0))
@ -153,12 +169,38 @@ func useProgramTexture(c *opengl.Context, projectionMatrix []float32, texture op
} }
} }
func useProgramRect(c *opengl.Context, projectionMatrix []float32) programFinisher { func useProgramLines(c *opengl.Context, projectionMatrix []float32) programFinisher {
if !lastProgram.Equals(programRect) { if !lastProgram.Equals(programSolid) {
c.UseProgram(programRect) c.UseProgram(programSolid)
lastProgram = programRect lastProgram = programSolid
} }
program := programRect program := programSolid
c.BindElementArrayBuffer(indexBufferLines)
c.UniformFloats(program, "projection_matrix", projectionMatrix)
c.EnableVertexAttribArray(program, "vertex")
c.EnableVertexAttribArray(program, "color")
// TODO: Change to floats?
c.VertexAttribPointer(program, "vertex", true, false, int16Size*6, 2, uintptr(int16Size*0))
c.VertexAttribPointer(program, "color", false, true, int16Size*6, 4, uintptr(int16Size*2))
return func() {
c.DisableVertexAttribArray(program, "color")
c.DisableVertexAttribArray(program, "vertex")
}
}
func useProgramRects(c *opengl.Context, projectionMatrix []float32) programFinisher {
if !lastProgram.Equals(programSolid) {
c.UseProgram(programSolid)
lastProgram = programSolid
}
program := programSolid
c.BindElementArrayBuffer(indexBufferQuads)
c.UniformFloats(program, "projection_matrix", projectionMatrix) c.UniformFloats(program, "projection_matrix", projectionMatrix)

View File

@ -25,7 +25,7 @@ const (
shaderVertex shaderId = iota shaderVertex shaderId = iota
shaderVertexColor shaderVertexColor
shaderFragmentTexture shaderFragmentTexture
shaderFragmentRect shaderFragmentSolid
) )
func shader(c *opengl.Context, id shaderId) string { func shader(c *opengl.Context, id shaderId) string {
@ -83,7 +83,7 @@ void main(void) {
gl_FragColor = color; gl_FragColor = color;
} }
`, `,
shaderFragmentRect: ` shaderFragmentSolid: `
varying lowp vec4 vertex_out_color; varying lowp vec4 vertex_out_color;
void main(void) { void main(void) {

View File

@ -54,7 +54,7 @@ func (t *Texture) Size() (width, height int) {
return t.width, t.height return t.width, t.height
} }
func NewTexture(c *opengl.Context, width, height int, filter opengl.FilterType) (*Texture, error) { func NewTexture(c *opengl.Context, width, height int, filter opengl.Filter) (*Texture, error) {
w := internal.NextPowerOf2Int(width) w := internal.NextPowerOf2Int(width)
h := internal.NextPowerOf2Int(height) h := internal.NextPowerOf2Int(height)
if w < 4 { if w < 4 {
@ -70,7 +70,7 @@ func NewTexture(c *opengl.Context, width, height int, filter opengl.FilterType)
return &Texture{native, width, height}, nil return &Texture{native, width, height}, nil
} }
func NewTextureFromImage(c *opengl.Context, img image.Image, filter opengl.FilterType) (*Texture, error) { func NewTextureFromImage(c *opengl.Context, img image.Image, filter opengl.Filter) (*Texture, error) {
origSize := img.Bounds().Size() origSize := img.Bounds().Size()
if origSize.X < 4 { if origSize.X < 4 {
return nil, errors.New("width must be equal or more than 4.") return nil, errors.New("width must be equal or more than 4.")

View File

@ -22,10 +22,13 @@ import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
) )
// TODO: Why int?
type Texture int type Texture int
type Framebuffer int type Framebuffer int
type Shader int type Shader int
type Program int type Program int
type Buffer gl.Buffer
// TODO: Remove this after the GopherJS bug was fixed (#159) // TODO: Remove this after the GopherJS bug was fixed (#159)
func (p Program) Equals(other Program) bool { func (p Program) Equals(other Program) bool {
@ -53,6 +56,8 @@ func NewContext() *Context {
ElementArrayBuffer: gl.ELEMENT_ARRAY_BUFFER, ElementArrayBuffer: gl.ELEMENT_ARRAY_BUFFER,
DynamicDraw: gl.DYNAMIC_DRAW, DynamicDraw: gl.DYNAMIC_DRAW,
StaticDraw: gl.STATIC_DRAW, StaticDraw: gl.STATIC_DRAW,
Triangles: gl.TRIANGLES,
Lines: gl.LINES,
} }
c.init() c.init()
return c return c
@ -65,7 +70,7 @@ func (c *Context) init() {
gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
} }
func (c *Context) NewTexture(width, height int, pixels []uint8, filter FilterType) (Texture, error) { func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
t := gl.GenTexture() t := gl.GenTexture()
if t < 0 { if t < 0 {
return 0, errors.New("glGenTexture failed") return 0, errors.New("glGenTexture failed")
@ -231,8 +236,9 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) {
l.DisableArray() l.DisableArray()
} }
func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageType BufferUsageType) { func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
gl.GenBuffer().Bind(gl.GLenum(bufferType)) b := gl.GenBuffer()
b.Bind(gl.GLenum(bufferType))
size := 0 size := 0
ptr := v ptr := v
switch v := v.(type) { switch v := v.(type) {
@ -246,7 +252,12 @@ func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageTyp
default: default:
panic("not reach") panic("not reach")
} }
gl.BufferData(gl.GLenum(bufferType), size, ptr, gl.GLenum(bufferUsageType)) gl.BufferData(gl.GLenum(bufferType), size, ptr, gl.GLenum(bufferUsage))
return Buffer(b)
}
func (c *Context) BindElementArrayBuffer(b Buffer) {
gl.Buffer(b).Bind(gl.ELEMENT_ARRAY_BUFFER)
} }
func (c *Context) BufferSubData(bufferType BufferType, data []int16) { func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
@ -254,8 +265,8 @@ func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
gl.BufferSubData(gl.GLenum(bufferType), 0, int16Size*len(data), data) gl.BufferSubData(gl.GLenum(bufferType), 0, int16Size*len(data), data)
} }
func (c *Context) DrawElements(len int) { func (c *Context) DrawElements(mode Mode, len int) {
gl.DrawElements(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, uintptr(0)) gl.DrawElements(gl.GLenum(mode), len, gl.UNSIGNED_SHORT, uintptr(0))
} }
func (c *Context) Flush() { func (c *Context) Flush() {

View File

@ -39,6 +39,10 @@ type Program struct {
js.Object js.Object
} }
type Buffer struct {
js.Object
}
// TODO: Remove this after the GopherJS bug was fixed (#159) // TODO: Remove this after the GopherJS bug was fixed (#159)
func (p Program) Equals(other Program) bool { func (p Program) Equals(other Program) bool {
return p.Object == other.Object return p.Object == other.Object
@ -62,14 +66,16 @@ type context struct {
func NewContext(gl *webgl.Context) *Context { func NewContext(gl *webgl.Context) *Context {
c := &Context{ c := &Context{
Nearest: FilterType(gl.NEAREST), Nearest: Filter(gl.NEAREST),
Linear: FilterType(gl.LINEAR), Linear: Filter(gl.LINEAR),
VertexShader: ShaderType(gl.VERTEX_SHADER), VertexShader: ShaderType(gl.VERTEX_SHADER),
FragmentShader: ShaderType(gl.FRAGMENT_SHADER), FragmentShader: ShaderType(gl.FRAGMENT_SHADER),
ArrayBuffer: BufferType(gl.ARRAY_BUFFER), ArrayBuffer: BufferType(gl.ARRAY_BUFFER),
ElementArrayBuffer: BufferType(gl.ELEMENT_ARRAY_BUFFER), ElementArrayBuffer: BufferType(gl.ELEMENT_ARRAY_BUFFER),
DynamicDraw: BufferUsageType(gl.DYNAMIC_DRAW), DynamicDraw: BufferUsage(gl.DYNAMIC_DRAW),
StaticDraw: BufferUsageType(gl.STATIC_DRAW), StaticDraw: BufferUsage(gl.STATIC_DRAW),
Triangles: Mode(gl.TRIANGLES),
Lines: Mode(gl.LINES),
} }
c.gl = gl c.gl = gl
c.init() c.init()
@ -83,7 +89,7 @@ func (c *Context) init() {
gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
} }
func (c *Context) NewTexture(width, height int, pixels []uint8, filter FilterType) (Texture, error) { func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
gl := c.gl gl := c.gl
t := gl.CreateTexture() t := gl.CreateTexture()
if t == nil { if t == nil {
@ -284,11 +290,17 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) {
gl.DisableVertexAttribArray(int(l)) gl.DisableVertexAttribArray(int(l))
} }
func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageType BufferUsageType) { func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
gl := c.gl gl := c.gl
b := gl.CreateBuffer() b := gl.CreateBuffer()
gl.BindBuffer(int(bufferType), b) gl.BindBuffer(int(bufferType), b)
gl.BufferData(int(bufferType), v, int(bufferUsageType)) gl.BufferData(int(bufferType), v, int(bufferUsage))
return Buffer{b}
}
func (c *Context) BindElementArrayBuffer(b Buffer) {
gl := c.gl
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.Object)
} }
func (c *Context) BufferSubData(bufferType BufferType, data []int16) { func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
@ -296,9 +308,9 @@ func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
gl.BufferSubData(int(bufferType), 0, data) gl.BufferSubData(int(bufferType), 0, data)
} }
func (c *Context) DrawElements(len int) { func (c *Context) DrawElements(mode Mode, len int) {
gl := c.gl gl := c.gl
gl.DrawElements(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, 0) gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, 0)
} }
func (c *Context) Flush() { func (c *Context) Flush() {

View File

@ -14,19 +14,22 @@
package opengl package opengl
type FilterType int type Filter int
type ShaderType int type ShaderType int
type BufferType int type BufferType int
type BufferUsageType int type BufferUsage int
type Mode int
type Context struct { type Context struct {
Nearest FilterType Nearest Filter
Linear FilterType Linear Filter
VertexShader ShaderType VertexShader ShaderType
FragmentShader ShaderType FragmentShader ShaderType
ArrayBuffer BufferType ArrayBuffer BufferType
ElementArrayBuffer BufferType ElementArrayBuffer BufferType
DynamicDraw BufferUsageType DynamicDraw BufferUsage
StaticDraw BufferUsageType StaticDraw BufferUsage
Triangles Mode
Lines Mode
context context
} }

View File

@ -1,4 +1,4 @@
// Copyright 2014 Hajime Hoshi // Copyright 2015 Hajime Hoshi
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -18,10 +18,35 @@ import (
"image/color" "image/color"
) )
// A Lines represents the set of lines.
type Lines interface {
Len() int
Points(i int) (x0, y0, x1, y1 int) // TODO: Change to float64?
Color(i int) color.Color
}
type line struct {
x0, y0 int
x1, y1 int
color color.Color
}
func (l *line) Len() int {
return 1
}
func (l *line) Points(i int) (x0, y0, x1, y1 int) {
return l.x0, l.y0, l.x1, l.y1
}
func (l *line) Color(i int) color.Color {
return l.color
}
// A Rects represents the set of rectangles. // A Rects represents the set of rectangles.
type Rects interface { type Rects interface {
Len() int Len() int
Rect(i int) (x, y, width, height int) Rect(i int) (x, y, width, height int) // TODO: Change to float64?
Color(i int) color.Color Color(i int) color.Color
} }