From f06d3564a1d8e083897118af2dace7b0be99ae5b Mon Sep 17 00:00:00 2001 From: wsw Date: Thu, 8 Dec 2016 11:16:19 +0800 Subject: [PATCH 1/3] apply current matrix when draw image --- context.go | 16 +++++++++++----- examples/rotated-image.go | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 examples/rotated-image.go diff --git a/context.go b/context.go index d62e2cb..8ea1372 100644 --- a/context.go +++ b/context.go @@ -4,14 +4,15 @@ package gg import ( "image" "image/color" - "image/draw" "image/png" "io" "math" "github.com/golang/freetype/raster" + "golang.org/x/image/draw" "golang.org/x/image/font" "golang.org/x/image/font/basicfont" + "golang.org/x/image/math/f64" ) type LineCap int @@ -567,12 +568,17 @@ func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) { s := im.Bounds().Size() x -= int(ax * float64(s.X)) y -= int(ay * float64(s.Y)) - p := image.Pt(x, y) - r := image.Rectangle{p, p.Add(s)} + transformer := draw.BiLinear + fx, fy := float64(x), float64(y) + m := dc.matrix.Translate(fx, fy) + s2d := f64.Aff3{m.XX, m.XY, m.X0, m.YX, m.YY, m.Y0} if dc.mask == nil { - draw.Draw(dc.im, r, im, image.ZP, draw.Over) + transformer.Transform(dc.im, s2d, im, im.Bounds(), draw.Over, nil) } else { - draw.DrawMask(dc.im, r, im, image.ZP, dc.mask, p, draw.Over) + transformer.Transform(dc.im, s2d, im, im.Bounds(), draw.Over, &draw.Options{ + DstMask: dc.mask, + DstMaskP: image.ZP, + }) } } diff --git a/examples/rotated-image.go b/examples/rotated-image.go new file mode 100644 index 0000000..ad478af --- /dev/null +++ b/examples/rotated-image.go @@ -0,0 +1,27 @@ +package main + +import "github.com/fogleman/gg" + +func main() { + const W = 400 + const H = 200 + im, err := gg.LoadPNG("examples/gopher.png") + if err != nil { + panic(err) + } + dc := gg.NewContext(W, H) + // draw outline + dc.SetHexColor("#ff0000") + dc.SetLineWidth(1) + dc.DrawRectangle(0, 0, float64(W), float64(H)) + dc.Stroke() + // draw image with current matrix applied + dc.SetHexColor("#0000ff") + dc.SetLineWidth(2) + dc.Rotate(gg.Radians(10)) + dc.DrawRectangle(100, 0, float64(im.Bounds().Dx()), float64(im.Bounds().Dy())/2) + dc.StrokePreserve() + dc.Clip() + dc.DrawImage(im, 100, 0) + dc.SavePNG("out.png") +} From 5e5aa690796de05ec62982db38edc8a8f65f9d85 Mon Sep 17 00:00:00 2001 From: wsw Date: Sun, 8 Jan 2017 16:18:14 +0800 Subject: [PATCH 2/3] update example 'rotated-image' --- examples/rotated-image.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/rotated-image.go b/examples/rotated-image.go index ad478af..0ce2544 100644 --- a/examples/rotated-image.go +++ b/examples/rotated-image.go @@ -4,24 +4,31 @@ import "github.com/fogleman/gg" func main() { const W = 400 - const H = 200 + const H = 500 im, err := gg.LoadPNG("examples/gopher.png") if err != nil { panic(err) } + iw, ih := im.Bounds().Dx(), im.Bounds().Dy() dc := gg.NewContext(W, H) // draw outline dc.SetHexColor("#ff0000") dc.SetLineWidth(1) dc.DrawRectangle(0, 0, float64(W), float64(H)) dc.Stroke() + // draw full image + dc.SetHexColor("#0000ff") + dc.SetLineWidth(2) + dc.DrawRectangle(100, 210, float64(iw), float64(ih)) + dc.Stroke() + dc.DrawImage(im, 100, 210) // draw image with current matrix applied dc.SetHexColor("#0000ff") dc.SetLineWidth(2) dc.Rotate(gg.Radians(10)) - dc.DrawRectangle(100, 0, float64(im.Bounds().Dx()), float64(im.Bounds().Dy())/2) + dc.DrawRectangle(100, 0, float64(iw), float64(ih)/2+20.0) dc.StrokePreserve() dc.Clip() - dc.DrawImage(im, 100, 0) + dc.DrawImageAnchored(im, 100, 0, 0.0, 0.0) dc.SavePNG("out.png") } From 2c35caba582611ac0393cc589134cc293a2a0782 Mon Sep 17 00:00:00 2001 From: wsw Date: Sun, 8 Jan 2017 16:23:33 +0800 Subject: [PATCH 3/3] apply current matrix when draw text --- context.go | 29 +++++++++++++++++++++++++---- examples/rotated-text.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 examples/rotated-text.go diff --git a/context.go b/context.go index e183a75..7093f6e 100644 --- a/context.go +++ b/context.go @@ -567,7 +567,6 @@ func (dc *Context) DrawRegularPolygon(n int, x, y, r, rotation float64) { } // DrawImage draws the specified image at the specified point. -// Currently, rotation and scaling transforms are not supported. func (dc *Context) DrawImage(im image.Image, x, y int) { dc.DrawImageAnchored(im, x, y, 0, 0) } @@ -615,11 +614,34 @@ func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) { Face: dc.fontFace, Dot: fixp(x, y), } - d.DrawString(s) + // based on Drawer.DrawString() in golang.org/x/image/font/font.go + prevC := rune(-1) + for _, c := range s { + if prevC >= 0 { + d.Dot.X += d.Face.Kern(prevC, c) + } + dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c) + if !ok { + // TODO: is falling back on the U+FFFD glyph the responsibility of + // the Drawer or the Face? + // TODO: set prevC = '\ufffd'? + continue + } + sr := dr.Sub(dr.Min) + transformer := draw.BiLinear + fx, fy := float64(dr.Min.X), float64(dr.Min.Y) + m := dc.matrix.Translate(fx, fy) + s2d := f64.Aff3{m.XX, m.XY, m.X0, m.YX, m.YY, m.Y0} + transformer.Transform(d.Dst, s2d, d.Src, sr, draw.Over, &draw.Options{ + SrcMask: mask, + SrcMaskP: maskp, + }) + d.Dot.X += advance + prevC = c + } } // DrawString draws the specified text at the specified point. -// Currently, rotation and scaling transforms are not supported. func (dc *Context) DrawString(s string, x, y float64) { dc.DrawStringAnchored(s, x, y, 0, 0) } @@ -629,7 +651,6 @@ func (dc *Context) DrawString(s string, x, y float64) { // text. Use ax=0.5, ay=0.5 to center the text at the specified point. func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) { w, h := dc.MeasureString(s) - x, y = dc.TransformPoint(x, y) x -= ax * w y += ay * h if dc.mask == nil { diff --git a/examples/rotated-text.go b/examples/rotated-text.go new file mode 100644 index 0000000..2a3171d --- /dev/null +++ b/examples/rotated-text.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/fogleman/gg" + "github.com/golang/freetype/truetype" + "golang.org/x/image/font/gofont/goregular" +) + +func main() { + const S = 400 + dc := gg.NewContext(S, S) + dc.SetRGB(1, 1, 1) + dc.Clear() + dc.SetRGB(0, 0, 0) + font, err := truetype.Parse(goregular.TTF) + if err != nil { + panic("") + } + face := truetype.NewFace(font, &truetype.Options{ + Size: 40, + }) + dc.SetFontFace(face) + text := "Hello, world!" + w, h := dc.MeasureString(text) + dc.Rotate(gg.Radians(10)) + dc.DrawRectangle(100, 180, w, h) + dc.Stroke() + dc.DrawStringAnchored(text, 100, 180, 0.0, 0.0) + dc.SavePNG("out.png") +}