aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/llgcode/draw2d/draw2dgl
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/llgcode/draw2d/draw2dgl')
-rw-r--r--vendor/github.com/llgcode/draw2d/draw2dgl/doc.go3
-rw-r--r--vendor/github.com/llgcode/draw2d/draw2dgl/gc.go411
-rw-r--r--vendor/github.com/llgcode/draw2d/draw2dgl/notes.md6
-rw-r--r--vendor/github.com/llgcode/draw2d/draw2dgl/text.go82
4 files changed, 502 insertions, 0 deletions
diff --git a/vendor/github.com/llgcode/draw2d/draw2dgl/doc.go b/vendor/github.com/llgcode/draw2d/draw2dgl/doc.go
new file mode 100644
index 0000000..0d7128a
--- /dev/null
+++ b/vendor/github.com/llgcode/draw2d/draw2dgl/doc.go
@@ -0,0 +1,3 @@
+// Package draw2dgl provides a graphic context that can draw vector
+// graphics and text on OpenGL.
+package draw2dgl
diff --git a/vendor/github.com/llgcode/draw2d/draw2dgl/gc.go b/vendor/github.com/llgcode/draw2d/draw2dgl/gc.go
new file mode 100644
index 0000000..d512ccc
--- /dev/null
+++ b/vendor/github.com/llgcode/draw2d/draw2dgl/gc.go
@@ -0,0 +1,411 @@
+package draw2dgl
+
+import (
+ "errors"
+ "image"
+ "image/color"
+ "image/draw"
+ "log"
+ "math"
+ "runtime"
+
+ "github.com/go-gl/gl/v2.1/gl"
+ "github.com/golang/freetype/raster"
+ "github.com/golang/freetype/truetype"
+ "github.com/llgcode/draw2d"
+ "github.com/llgcode/draw2d/draw2dbase"
+ "github.com/llgcode/draw2d/draw2dimg"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func init() {
+ runtime.LockOSThread()
+}
+
+type Painter struct {
+ // The Porter-Duff composition operator.
+ Op draw.Op
+ // The 16-bit color to paint the spans.
+ cr, cg, cb uint8
+ ca uint32
+ colors []uint8
+ vertices []int32
+}
+
+const M16 uint32 = 1<<16 - 1
+
+// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
+func (p *Painter) Paint(ss []raster.Span, done bool) {
+ //gl.Begin(gl.LINES)
+ sslen := len(ss)
+ clenrequired := sslen * 8
+ vlenrequired := sslen * 4
+ if clenrequired >= (cap(p.colors) - len(p.colors)) {
+ p.Flush()
+
+ if clenrequired >= cap(p.colors) {
+ p.vertices = make([]int32, 0, vlenrequired+(vlenrequired/2))
+ p.colors = make([]uint8, 0, clenrequired+(clenrequired/2))
+ }
+ }
+ vi := len(p.vertices)
+ ci := len(p.colors)
+ p.vertices = p.vertices[0 : vi+vlenrequired]
+ p.colors = p.colors[0 : ci+clenrequired]
+ var (
+ colors []uint8
+ vertices []int32
+ )
+ for _, s := range ss {
+ a := uint8((s.Alpha * p.ca / M16) >> 8)
+
+ colors = p.colors[ci:]
+ colors[0] = p.cr
+ colors[1] = p.cg
+ colors[2] = p.cb
+ colors[3] = a
+ colors[4] = p.cr
+ colors[5] = p.cg
+ colors[6] = p.cb
+ colors[7] = a
+ ci += 8
+ vertices = p.vertices[vi:]
+ vertices[0] = int32(s.X0)
+ vertices[1] = int32(s.Y)
+ vertices[2] = int32(s.X1)
+ vertices[3] = int32(s.Y)
+ vi += 4
+ }
+}
+
+func (p *Painter) Flush() {
+ if len(p.vertices) != 0 {
+ gl.EnableClientState(gl.COLOR_ARRAY)
+ gl.EnableClientState(gl.VERTEX_ARRAY)
+ gl.ColorPointer(4, gl.UNSIGNED_BYTE, 0, gl.Ptr(p.colors))
+ gl.VertexPointer(2, gl.INT, 0, gl.Ptr(p.vertices))
+
+ // draw lines
+ gl.DrawArrays(gl.LINES, 0, int32(len(p.vertices)/2))
+ gl.DisableClientState(gl.VERTEX_ARRAY)
+ gl.DisableClientState(gl.COLOR_ARRAY)
+ p.vertices = p.vertices[0:0]
+ p.colors = p.colors[0:0]
+ }
+}
+
+// SetColor sets the color to paint the spans.
+func (p *Painter) SetColor(c color.Color) {
+ r, g, b, a := c.RGBA()
+ if a == 0 {
+ p.cr = 0
+ p.cg = 0
+ p.cb = 0
+ p.ca = a
+ } else {
+ p.cr = uint8((r * M16 / a) >> 8)
+ p.cg = uint8((g * M16 / a) >> 8)
+ p.cb = uint8((b * M16 / a) >> 8)
+ p.ca = a
+ }
+}
+
+// NewRGBAPainter creates a new RGBAPainter for the given image.
+func NewPainter() *Painter {
+ p := new(Painter)
+ p.vertices = make([]int32, 0, 1024)
+ p.colors = make([]uint8, 0, 1024)
+ return p
+}
+
+type GraphicContext struct {
+ *draw2dbase.StackGraphicContext
+ painter *Painter
+ fillRasterizer *raster.Rasterizer
+ strokeRasterizer *raster.Rasterizer
+ glyphBuf *truetype.GlyphBuf
+ DPI int
+}
+
+// NewGraphicContext creates a new Graphic context from an image.
+func NewGraphicContext(width, height int) *GraphicContext {
+ gc := &GraphicContext{
+ draw2dbase.NewStackGraphicContext(),
+ NewPainter(),
+ raster.NewRasterizer(width, height),
+ raster.NewRasterizer(width, height),
+ &truetype.GlyphBuf{},
+ 92,
+ }
+ return gc
+}
+
+func (gc *GraphicContext) loadCurrentFont() (*truetype.Font, error) {
+ font := draw2d.GetFont(gc.Current.FontData)
+ if font == nil {
+ font = draw2d.GetFont(draw2dbase.DefaultFontData)
+ }
+ if font == nil {
+ return nil, errors.New("No font set, and no default font available.")
+ }
+ gc.SetFont(font)
+ gc.SetFontSize(gc.Current.FontSize)
+ return font, nil
+}
+
+func (gc *GraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
+ if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), glyph, font.HintingNone); err != nil {
+ return err
+ }
+ e0 := 0
+ for _, e1 := range gc.glyphBuf.Ends {
+ DrawContour(gc, gc.glyphBuf.Points[e0:e1], dx, dy)
+ e0 = e1
+ }
+ return nil
+}
+
+// CreateStringPath creates a path from the string s at x, y, and returns the string width.
+// The text is placed so that the left edge of the em square of the first character of s
+// and the baseline intersect at x, y. The majority of the affected pixels will be
+// above and to the right of the point, but some may be below or to the left.
+// For example, drawing a string that starts with a 'J' in an italic font may
+// affect pixels below and left of the point.
+func (gc *GraphicContext) CreateStringPath(s string, x, y float64) float64 {
+ f, err := gc.loadCurrentFont()
+ if err != nil {
+ log.Println(err)
+ return 0.0
+ }
+ startx := x
+ prev, hasPrev := truetype.Index(0), false
+ for _, rune := range s {
+ index := f.Index(rune)
+ if hasPrev {
+ x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
+ }
+ err := gc.drawGlyph(index, x, y)
+ if err != nil {
+ log.Println(err)
+ return startx - x
+ }
+ x += fUnitsToFloat64(f.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth)
+ prev, hasPrev = index, true
+ }
+ return x - startx
+}
+
+// FillString draws the text at point (0, 0)
+func (gc *GraphicContext) FillString(text string) (width float64) {
+ return gc.FillStringAt(text, 0, 0)
+}
+
+// FillStringAt draws the text at the specified point (x, y)
+func (gc *GraphicContext) FillStringAt(text string, x, y float64) (width float64) {
+ f, err := gc.loadCurrentFont()
+ if err != nil {
+ log.Println(err)
+ return 0.0
+ }
+ startx := x
+ prev, hasPrev := truetype.Index(0), false
+ fontName := gc.GetFontName()
+ for _, r := range text {
+ index := f.Index(r)
+ if hasPrev {
+ x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
+ }
+ glyph := draw2dbase.FetchGlyph(gc, fontName, r)
+ x += glyph.Fill(gc, x, y)
+ prev, hasPrev = index, true
+ }
+ return x - startx
+}
+
+// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
+// The the left edge of the em square of the first character of s
+// and the baseline intersect at 0, 0 in the returned coordinates.
+// Therefore the top and left coordinates may well be negative.
+func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
+ f, err := gc.loadCurrentFont()
+ if err != nil {
+ log.Println(err)
+ return 0, 0, 0, 0
+ }
+ top, left, bottom, right = 10e6, 10e6, -10e6, -10e6
+ cursor := 0.0
+ prev, hasPrev := truetype.Index(0), false
+ for _, rune := range s {
+ index := f.Index(rune)
+ if hasPrev {
+ cursor += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
+ }
+ if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), index, font.HintingNone); err != nil {
+ log.Println(err)
+ return 0, 0, 0, 0
+ }
+ e0 := 0
+ for _, e1 := range gc.glyphBuf.Ends {
+ ps := gc.glyphBuf.Points[e0:e1]
+ for _, p := range ps {
+ x, y := pointToF64Point(p)
+ top = math.Min(top, y)
+ bottom = math.Max(bottom, y)
+ left = math.Min(left, x+cursor)
+ right = math.Max(right, x+cursor)
+ }
+ }
+ cursor += fUnitsToFloat64(f.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth)
+ prev, hasPrev = index, true
+ }
+ return left, top, right, bottom
+}
+
+// StrokeString draws the contour of the text at point (0, 0)
+func (gc *GraphicContext) StrokeString(text string) (width float64) {
+ return gc.StrokeStringAt(text, 0, 0)
+}
+
+// StrokeStringAt draws the contour of the text at point (x, y)
+func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (width float64) {
+ f, err := gc.loadCurrentFont()
+ if err != nil {
+ log.Println(err)
+ return 0.0
+ }
+ startx := x
+ prev, hasPrev := truetype.Index(0), false
+ fontName := gc.GetFontName()
+ for _, r := range text {
+ index := f.Index(r)
+ if hasPrev {
+ x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
+ }
+ glyph := draw2dbase.FetchGlyph(gc, fontName, r)
+ x += glyph.Stroke(gc, x, y)
+ prev, hasPrev = index, true
+ }
+ return x - startx
+}
+
+// recalc recalculates scale and bounds values from the font size, screen
+// resolution and font metrics, and invalidates the glyph cache.
+func (gc *GraphicContext) recalc() {
+ gc.Current.Scale = gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0)
+}
+
+func (gc *GraphicContext) SetDPI(dpi int) {
+ gc.DPI = dpi
+ gc.recalc()
+}
+
+// SetFont sets the font used to draw text.
+func (gc *GraphicContext) SetFont(font *truetype.Font) {
+ gc.Current.Font = font
+}
+
+// SetFontSize sets the font size in points (as in ``a 12 point font'').
+func (gc *GraphicContext) SetFontSize(fontSize float64) {
+ gc.Current.FontSize = fontSize
+ gc.recalc()
+}
+
+func (gc *GraphicContext) GetDPI() int {
+ return gc.DPI
+}
+
+//TODO
+func (gc *GraphicContext) Clear() {
+ panic("not implemented")
+}
+
+//TODO
+func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
+ panic("not implemented")
+}
+
+//TODO
+func (gc *GraphicContext) DrawImage(img image.Image) {
+ panic("not implemented")
+}
+
+func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
+ gc.painter.SetColor(color)
+ rasterizer.Rasterize(gc.painter)
+ rasterizer.Clear()
+ gc.painter.Flush()
+ gc.Current.Path.Clear()
+}
+
+func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
+ paths = append(paths, gc.Current.Path)
+ gc.strokeRasterizer.UseNonZeroWinding = true
+
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: draw2dimg.FtLineBuilder{Adder: gc.strokeRasterizer}})
+ stroker.HalfLineWidth = gc.Current.LineWidth / 2
+
+ var liner draw2dbase.Flattener
+ if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
+ liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
+ } else {
+ liner = stroker
+ }
+ for _, p := range paths {
+ draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
+ }
+
+ gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
+}
+
+func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
+ paths = append(paths, gc.Current.Path)
+ gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
+
+ /**** first method ****/
+ flattener := draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: draw2dimg.FtLineBuilder{Adder: gc.fillRasterizer}}
+ for _, p := range paths {
+ draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
+ }
+
+ gc.paint(gc.fillRasterizer, gc.Current.FillColor)
+}
+
+func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
+ paths = append(paths, gc.Current.Path)
+ gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
+ gc.strokeRasterizer.UseNonZeroWinding = true
+
+ flattener := draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: draw2dimg.FtLineBuilder{Adder: gc.fillRasterizer}}
+
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: draw2dimg.FtLineBuilder{Adder: gc.strokeRasterizer}})
+ stroker.HalfLineWidth = gc.Current.LineWidth / 2
+
+ var liner draw2dbase.Flattener
+ if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
+ liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
+ } else {
+ liner = stroker
+ }
+
+ demux := draw2dbase.DemuxFlattener{Flatteners: []draw2dbase.Flattener{flattener, liner}}
+ for _, p := range paths {
+ draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
+ }
+
+ // Fill
+ gc.paint(gc.fillRasterizer, gc.Current.FillColor)
+ // Stroke
+ gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
+}
+
+func useNonZeroWinding(f draw2d.FillRule) bool {
+ switch f {
+ case draw2d.FillRuleEvenOdd:
+ return false
+ case draw2d.FillRuleWinding:
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/llgcode/draw2d/draw2dgl/notes.md b/vendor/github.com/llgcode/draw2d/draw2dgl/notes.md
new file mode 100644
index 0000000..0c21456
--- /dev/null
+++ b/vendor/github.com/llgcode/draw2d/draw2dgl/notes.md
@@ -0,0 +1,6 @@
+Rendering Vector Art OpenGl
+References:
+* https://www.youtube.com/watch?v=K5u8ZZBWSdw
+* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html
+* http://research.microsoft.com/en-us/um/people/cloop/loopblinn05.pdf
+* https://github.com/openframeworks/openFrameworks/issues/1190
diff --git a/vendor/github.com/llgcode/draw2d/draw2dgl/text.go b/vendor/github.com/llgcode/draw2d/draw2dgl/text.go
new file mode 100644
index 0000000..79cd8c8
--- /dev/null
+++ b/vendor/github.com/llgcode/draw2d/draw2dgl/text.go
@@ -0,0 +1,82 @@
+package draw2dgl
+
+import (
+ "github.com/golang/freetype/truetype"
+ "github.com/llgcode/draw2d"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// DrawContour draws the given closed contour at the given sub-pixel offset.
+func DrawContour(path draw2d.PathBuilder, ps []truetype.Point, dx, dy float64) {
+ if len(ps) == 0 {
+ return
+ }
+ startX, startY := pointToF64Point(ps[0])
+ path.MoveTo(startX+dx, startY+dy)
+ q0X, q0Y, on0 := startX, startY, true
+ for _, p := range ps[1:] {
+ qX, qY := pointToF64Point(p)
+ on := p.Flags&0x01 != 0
+ if on {
+ if on0 {
+ path.LineTo(qX+dx, qY+dy)
+ } else {
+ path.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
+ }
+ } else {
+ if on0 {
+ // No-op.
+ } else {
+ midX := (q0X + qX) / 2
+ midY := (q0Y + qY) / 2
+ path.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
+ }
+ }
+ q0X, q0Y, on0 = qX, qY, on
+ }
+ // Close the curve.
+ if on0 {
+ path.LineTo(startX+dx, startY+dy)
+ } else {
+ path.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy)
+ }
+}
+
+func pointToF64Point(p truetype.Point) (x, y float64) {
+ return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y)
+}
+
+func fUnitsToFloat64(x fixed.Int26_6) float64 {
+ scaled := x << 2
+ return float64(scaled/256) + float64(scaled%256)/256.0
+}
+
+// FontExtents contains font metric information.
+type FontExtents struct {
+ // Ascent is the distance that the text
+ // extends above the baseline.
+ Ascent float64
+
+ // Descent is the distance that the text
+ // extends below the baseline. The descent
+ // is given as a negative value.
+ Descent float64
+
+ // Height is the distance from the lowest
+ // descending point to the highest ascending
+ // point.
+ Height float64
+}
+
+// Extents returns the FontExtents for a font.
+// TODO needs to read this https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#intro
+func Extents(font *truetype.Font, size float64) FontExtents {
+ bounds := font.Bounds(fixed.Int26_6(font.FUnitsPerEm()))
+ scale := size / float64(font.FUnitsPerEm())
+ return FontExtents{
+ Ascent: float64(bounds.Max.Y) * scale,
+ Descent: float64(bounds.Min.Y) * scale,
+ Height: float64(bounds.Max.Y-bounds.Min.Y) * scale,
+ }
+}