diff --git a/vg/vgimg/vgimg.go b/vg/vgimg/vgimg.go index 5558e1d493fffe0ab46c141c3c10bcf507cf0a55..c35510f71ac09f72f1d44df56cff77b347c500b6 100644 --- a/vg/vgimg/vgimg.go +++ b/vg/vgimg/vgimg.go @@ -53,13 +53,22 @@ func New(width, height vg.Length) *Canvas { // minimum point of the given image // should probably be 0,0. func NewImage(img draw.Image) *Canvas { - w := float64(img.Bounds().Max.X - img.Bounds().Min.X) h := float64(img.Bounds().Max.Y - img.Bounds().Min.Y) - draw.Draw(img, img.Bounds(), image.White, image.ZP, draw.Src) gc := draw2d.NewGraphicContext(img) gc.SetDPI(dpi) gc.Scale(1, -1) gc.Translate(0, -h) + return NewImageWithContext(img, gc) +} + +// NewImageWithContext returns a new image canvas +// that draws to the given image, using the given graphic context. +// The minimum point of the given image +// should probably be 0,0. +func NewImageWithContext(img draw.Image, gc draw2d.GraphicContext) *Canvas { + w := float64(img.Bounds().Max.X - img.Bounds().Min.X) + h := float64(img.Bounds().Max.Y - img.Bounds().Min.Y) + draw.Draw(img, img.Bounds(), image.White, image.ZP, draw.Src) c := &Canvas{ gc: gc, img: img, diff --git a/vg/vgx11/canvas.go b/vg/vgx11/canvas.go new file mode 100644 index 0000000000000000000000000000000000000000..3a0d2ada4347e3ca9fe574351dfb7a4a7da1b457 --- /dev/null +++ b/vg/vgx11/canvas.go @@ -0,0 +1,92 @@ +// Copyright ©2015 The gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +// The vgx11 package uses xgbutil (github.com/BurntSushi/xgbutil) +// as a X11-display backend for vg. +package vgx11 + +import ( + "image" + "image/draw" + + "code.google.com/p/draw2d/draw2d" + "github.com/BurntSushi/xgbutil" + "github.com/BurntSushi/xgbutil/keybind" + "github.com/BurntSushi/xgbutil/xevent" + "github.com/BurntSushi/xgbutil/xgraphics" + "github.com/BurntSushi/xgbutil/xwindow" + "github.com/gonum/plot/vg" + "github.com/gonum/plot/vg/vgimg" +) + +// dpi is the number of dots per inch. +const dpi = 96 + +// Canvas implements the vg.Canvas interface, +// drawing to an image.Image using draw2d. +type Canvas struct { + *vgimg.Canvas + + // X window values + x *xgbutil.XUtil + ximg *xgraphics.Image + wid *xwindow.Window +} + +// New returns a new image canvas with +// the size specified rounded up to the +// nearest pixel. +func New(width, height vg.Length, name string) (*Canvas, error) { + w := width / vg.Inch * dpi + h := height / vg.Inch * dpi + img := image.NewRGBA(image.Rect(0, 0, int(w+0.5), int(h+0.5))) + + return NewImage(img, name) +} + +// NewImage returns a new image canvas +// that draws to the given image. The +// minimum point of the given image +// should probably be 0,0. +func NewImage(img draw.Image, name string) (*Canvas, error) { + w := float64(img.Bounds().Max.X - img.Bounds().Min.X) + h := float64(img.Bounds().Max.Y - img.Bounds().Min.Y) + + X, err := xgbutil.NewConn() + if err != nil { + return nil, err + } + keybind.Initialize(X) + ximg := xgraphics.New(X, image.Rect(0, 0, int(w), int(h))) + err = ximg.CreatePixmap() + if err != nil { + return nil, err + } + painter := NewPainter(ximg) + gc := draw2d.NewGraphicContextWithPainter(ximg, painter) + gc.SetDPI(dpi) + gc.Scale(1, -1) + gc.Translate(0, -h) + + wid := ximg.XShowExtra(name, true) + go func() { + xevent.Main(X) + }() + + c := &Canvas{ + Canvas: vgimg.NewImageWithContext(img, gc), + x: X, + ximg: ximg, + wid: wid, + } + vg.Initialize(c) + return c, nil +} + +func (c *Canvas) Paint() { + c.ximg.XDraw() + c.ximg.XPaint(c.wid.Id) +} diff --git a/vg/vgx11/main.go b/vg/vgx11/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7365061914cff8bc621fbf9502ca825660f23a65 --- /dev/null +++ b/vg/vgx11/main.go @@ -0,0 +1,80 @@ +// Copyright ©2015 The gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//+build ignore + +package main + +import ( + "math/rand" + "time" + + "github.com/gonum/plot" + "github.com/gonum/plot/plotter" + "github.com/gonum/plot/plotutil" + "github.com/gonum/plot/vg/draw" + "github.com/gonum/plot/vg/vgx11" +) + +func main() { + rand.Seed(int64(0)) + + p, err := plot.New() + if err != nil { + panic(err) + } + + p.Title.Text = "Plotutil example" + p.X.Label.Text = "X" + p.Y.Label.Text = "Y" + + err = plotutil.AddLinePoints( + p, + "First", randomPoints(15), + ) + if err != nil { + panic(err) + } + + cnvs, err := vgx11.New(4*96, 4*96, "Example") + if err != nil { + panic(err) + } + + p.Draw(draw.New(cnvs)) + cnvs.Paint() + time.Sleep(5 * time.Second) + + err = plotutil.AddLinePoints( + p, + "Second", randomPoints(15), + "Third", randomPoints(15), + ) + if err != nil { + panic(err) + } + + p.Draw(draw.New(cnvs)) + cnvs.Paint() + time.Sleep(10 * time.Second) + + // Save the plot to a PNG file. + // if err := p.Save(4, 4, "points.png"); err != nil { + // panic(err) + // } +} + +// randomPoints returns some random x, y points. +func randomPoints(n int) plotter.XYs { + pts := make(plotter.XYs, n) + for i := range pts { + if i == 0 { + pts[i].X = rand.Float64() + } else { + pts[i].X = pts[i-1].X + rand.Float64() + } + pts[i].Y = pts[i].X + 10*rand.Float64() + } + return pts +} diff --git a/vg/vgx11/painter.go b/vg/vgx11/painter.go new file mode 100644 index 0000000000000000000000000000000000000000..d661fa1f60dce666dda4d9868f5dea03d3b30737 --- /dev/null +++ b/vg/vgx11/painter.go @@ -0,0 +1,99 @@ +// Copyright ©2015 The gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +// Very liberally copied the code from freetype-go (file: paint.go) +// Adapted to the *xgraphics.Image type. + +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. +// +// Use of the Freetype-Go software is subject to your choice of exactly one of +// the following two licenses: +// * The FreeType License, which is similar to the original BSD license with +// an advertising clause, or +// * The GNU General Public License (GPL), version 2 or later. +// +// The text of these licenses are available in the licenses/ftl.txt and the +// licenses/gpl.txt files respectively. They are also available at +// http://freetype.sourceforge.net/license.html + +package vgx11 + +import ( + "image/color" + "image/draw" + + "code.google.com/p/freetype-go/freetype/raster" + "github.com/BurntSushi/xgbutil/xgraphics" +) + +type Painter struct { + // The image to compose onto. + Image *xgraphics.Image + // The Porter-Duff composition operator. + Op draw.Op + // The 16-bit color to paint the spans. + cr, cg, cb, ca uint32 +} + +// Paint satisfies the Painter interface by painting ss onto an xgraphics.Image. +func (r *Painter) Paint(ss []raster.Span, done bool) { + b := r.Image.Bounds() + for _, s := range ss { + if s.Y < b.Min.Y { + continue + } + if s.Y >= b.Max.Y { + return + } + if s.X0 < b.Min.X { + s.X0 = b.Min.X + } + if s.X1 > b.Max.X { + s.X1 = b.Max.X + } + if s.X0 >= s.X1 { + continue + } + // This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go. + ma := s.A >> 16 + const m = 1<<16 - 1 + i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4 + i1 := i0 + (s.X1-s.X0)*4 + if r.Op == draw.Over { + for i := i0; i < i1; i += 4 { + dr := uint32(r.Image.Pix[i+0]) + dg := uint32(r.Image.Pix[i+1]) + db := uint32(r.Image.Pix[i+2]) + da := uint32(r.Image.Pix[i+3]) + a := (m - (r.ca * ma / m)) * 0x101 + r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8) + r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8) + r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8) + r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8) + } + } else { + for i := i0; i < i1; i += 4 { + r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8) + r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8) + r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8) + r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8) + } + } + } +} + +// SetColor sets the color to paint the spans. +func (r *Painter) SetColor(c color.Color) { + r.cr, r.cg, r.cb, r.ca = c.RGBA() +} + +// NewPainter creates a new Painter for the given image. +func NewPainter(img *xgraphics.Image) *Painter { + return &Painter{Image: img} +}