Skip to content

Commit

Permalink
Add image support to GL renderer
Browse files Browse the repository at this point in the history
Moved from vertex colors to textures for drawing to support this
  • Loading branch information
andydotxyz committed Nov 19, 2018
1 parent 72d0e4d commit f935c33
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 45 deletions.
6 changes: 6 additions & 0 deletions cmd/fyne_demo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "fmt"

import "github.com/fyne-io/fyne"
import "github.com/fyne-io/fyne/app"
import "github.com/fyne-io/fyne/canvas"
import "github.com/fyne-io/fyne/layout"
import "github.com/fyne-io/fyne/theme"
import "github.com/fyne-io/fyne/dialog"
Expand Down Expand Up @@ -43,6 +44,9 @@ func main() {
entry := widget.NewEntry()
entry.Text = "Entry"

cv := canvas.NewImageFromResource(theme.FyneLogo())
cv.SetMinSize(fyne.NewSize(64, 64))

w.SetContent(widget.NewVBox(
widget.NewToolbar(widget.NewToolbarAction(theme.MailComposeIcon(), func() { fmt.Println("New") }),
widget.NewToolbarSeparator(),
Expand All @@ -64,6 +68,8 @@ func main() {
widget.NewButton("Form", func() { formApp(app) }),
),

widget.NewHBox(layout.NewSpacer(), cv, layout.NewSpacer()),

widget.NewGroup("Dialogs",
widget.NewButton("Info", func() {
dialog.ShowInformation("Information", "You should know this thing...", w)
Expand Down
154 changes: 111 additions & 43 deletions driver/gl/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ import (
"fmt"
"github.com/fyne-io/fyne"
"github.com/fyne-io/fyne/canvas"
"github.com/fyne-io/fyne/theme"
"github.com/go-gl/gl/v3.3-core/gl"
"image/draw"
"log"
"os"
"path/filepath"

"image"
"image/color"
_ "image/png"
"strings"
)

Expand Down Expand Up @@ -66,46 +74,32 @@ const (
vertexShaderSource = `
#version 130
in vec3 vp;
uniform vec4 obj_fill_color;
out vec4 fill_color;
in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
fill_color = obj_fill_color;
fragTexCoord = vertTexCoord;
gl_Position = vec4(vp, 1.0);
}
` + "\x00"

fragmentShaderSource = `
#version 130
in vec4 fill_color;
in vec2 fragTexCoord;
out vec4 frag_colour;
uniform sampler2D tex;
void main() {
frag_colour = fill_color;
vec4 color = texture(tex, fragTexCoord);
if(color.a < 0.01)
discard;
frag_colour = color;
}
` + "\x00"
)

func square(size fyne.Size, pos fyne.Position, full fyne.Size) []float32 {
xPos := float32(pos.X) / float32(full.Width)
x1 := -1 + xPos*2
x2Pos := float32(pos.X+size.Width) / float32(full.Width)
x2 := -1 + x2Pos*2

yPos := float32(pos.Y) / float32(full.Height)
y1 := 1 - yPos*2
y2Pos := float32(pos.Y+size.Height) / float32(full.Height)
y2 := 1 - y2Pos*2

return []float32{
x1, y1, 0,
x1, y2, 0,
x2, y2, 0,

x1, y1, 0,
x2, y1, 0,
x2, y2, 0,
}
}

func (d *gLDriver) initOpenGL() {
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
if err != nil {
Expand All @@ -124,44 +118,118 @@ func (d *gLDriver) initOpenGL() {
d.program = prog
}

// makeVao initializes and returns a vertex array from the points provided.
func (c *glCanvas) makeVao(points []float32, fill color.Color) uint32 {
// rectCoords calculates the openGL coordinate space of a rectangle
func (c *glCanvas) rectCoords(size fyne.Size, pos fyne.Position) []float32 {
xPos := float32(pos.X) / float32(c.Size().Width)
x1 := -1 + xPos*2
x2Pos := float32(pos.X+size.Width) / float32(c.Size().Width)
x2 := -1 + x2Pos*2

yPos := float32(pos.Y) / float32(c.Size().Height)
y1 := 1 - yPos*2
y2Pos := float32(pos.Y+size.Height) / float32(c.Size().Height)
y2 := 1 - y2Pos*2

return []float32{
// coord x, y, x texture x, y
x1, y2, 0, 0.0, 1.0, // top left
x1, y1, 0, 0.0, 0.0, // bottom left
x2, y2, 0, 1.0, 1.0, // top right
x2, y1, 0, 1.0, 0.0, // bottom right
}
}

// textureForPoints initializes a vertex array and prepares a texture to draw on it
func (c *glCanvas) textureForPoints(points []float32) {
var vbo uint32
gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)

r, g, b, a := fill.RGBA()
loc := gl.GetUniformLocation(c.program, gl.Str("obj_fill_color\x00"))
gl.Uniform4f(loc, float32(uint8(r))/255,
float32(uint8(g))/255, float32(uint8(b))/255, float32(uint8(a))/255)

var vao uint32
gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
gl.EnableVertexAttribArray(0)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, nil)

return vao
}
textureUniform := gl.GetUniformLocation(c.program, gl.Str("tex\x00"))
gl.Uniform1i(textureUniform, 0)

texCoordAttrib := uint32(gl.GetAttribLocation(c.program, gl.Str("vertTexCoord\x00")))
gl.EnableVertexAttribArray(texCoordAttrib)
gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4))

func draw(vao uint32, len int) {
gl.BindVertexArray(vao)
gl.DrawArrays(gl.TRIANGLES, 0, int32(len/3))

var texture uint32
gl.GenTextures(1, &texture)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
}

func (c *glCanvas) drawRectangle(rect *canvas.Rectangle, pos fyne.Position) {
// Add the padding so that our calculations fit in a smaller area
points := square(rect.Size, pos, c.Size())
square := c.makeVao(points, rect.FillColor)
draw(square, len(points))
points := c.rectCoords(rect.Size, pos)
c.textureForPoints(points)

r, g, b, a := rect.FillColor.RGBA()
data := []uint8{uint8(r), uint8(g), uint8(b), uint8(a)}
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA,
gl.UNSIGNED_BYTE, gl.Ptr(data))

gl.DrawArrays(gl.TRIANGLE_STRIP, 0, int32(len(points)/5))
}

func (c *glCanvas) drawRawImage(img *image.RGBA, size fyne.Size, pos fyne.Position) {
points := c.rectCoords(size, pos)
c.textureForPoints(points)

gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(img.Rect.Size().X), int32(img.Rect.Size().Y),
0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(img.Pix))

gl.DrawArrays(gl.TRIANGLE_STRIP, 0, int32(len(points)/5))
}

func (c *glCanvas) drawImage(img *canvas.Image, pos fyne.Position) {
raw := image.NewRGBA(image.Rect(0, 0, img.Size.Width, img.Size.Height))

if img.File != "" {
if strings.ToLower(filepath.Ext(img.File)) == ".svg" {
placeholderColor := &image.Uniform{color.RGBA{0, 0, 255, 255}}
draw.Draw(raw, raw.Bounds(), placeholderColor, image.ZP, draw.Src)
} else {
file, _ := os.Open(img.File)
pixels, _, err := image.Decode(file)

if err != nil {
log.Println("image err", err)

errColor := &image.Uniform{color.RGBA{255, 0, 0, 255}}
draw.Draw(raw, raw.Bounds(), errColor, image.ZP, draw.Src)
} else {
raw = image.NewRGBA(pixels.Bounds())

draw.Draw(raw, raw.Bounds(), pixels, image.ZP, draw.Src)
}
}
} else if img.PixelColor != nil {
pixels := NewPixelImage(img)
draw.Draw(raw, raw.Bounds(), pixels, image.ZP, draw.Src)
}

c.drawRawImage(raw, img.Size, pos)
}

func (c *glCanvas) drawObject(o fyne.CanvasObject, offset fyne.Position) {
pos := o.CurrentPosition().Add(offset)
switch obj := o.(type) {
case *canvas.Rectangle:
c.drawRectangle(obj, pos)
case *canvas.Image:
c.drawImage(obj, pos)
}
}
33 changes: 33 additions & 0 deletions driver/gl/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// +build !ci,gl

package gl

import (
"github.com/fyne-io/fyne/canvas"
"image"
"image/color"
)

type pixelImage struct {
source *canvas.Image
}

func (i *pixelImage) ColorModel() color.Model {
return color.RGBAModel
}

func (i *pixelImage) Bounds() image.Rectangle {
return image.Rect(0, 0, i.source.Size.Width, i.source.Size.Height)
}

func (i *pixelImage) At(x, y int) color.Color {
if i.source.PixelColor == nil {
return color.Transparent
}

return i.source.PixelColor(x, y, i.source.Size.Width, i.source.Size.Height)
}

func NewPixelImage(source *canvas.Image) image.Image {
return &pixelImage{source: source}
}
2 changes: 1 addition & 1 deletion driver/gl/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (w *window) SetFixedSize(fixed bool) {
if fixed {
w.viewport.SetSizeLimits(winWidth, winHeight, winWidth, winHeight)
} else {
w.viewport.SetSizeLimits(winWidth, winHeight, 10000, 10000)
w.viewport.SetSizeLimits(winWidth, winHeight, glfw.DontCare, glfw.DontCare)
}
}

Expand Down
2 changes: 1 addition & 1 deletion widget/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (l *Label) RowLength(row int) int {
func (l *Label) createRenderer() fyne.WidgetRenderer {
render := &labelRenderer{label: l}

render.background = canvas.NewRectangle(theme.ButtonColor())
render.background = canvas.NewRectangle(theme.BackgroundColor())
render.texts = []*canvas.Text{}
render.objects = []fyne.CanvasObject{render.background}
render.updateTexts(render.parseText(l.Text))
Expand Down

0 comments on commit f935c33

Please sign in to comment.