draw2d/draw2dbase/stack_gc.go

321 lines
8.7 KiB
Go
Raw Normal View History

2011-04-27 08:06:14 +00:00
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
2012-04-17 09:03:56 +00:00
package draw2dbase
2011-04-27 08:06:14 +00:00
import (
"errors"
2011-04-27 08:06:14 +00:00
"image"
2012-01-13 09:14:12 +00:00
"image/color"
"log"
"math"
2015-04-16 09:51:13 +00:00
2015-07-09 16:06:14 +00:00
"github.com/llgcode/draw2d"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
2011-04-27 08:06:14 +00:00
)
2015-08-14 20:38:18 +00:00
var DefaultFontData = draw2d.FontData{Name: "luxi", Family: draw2d.FontFamilySans, Style: draw2d.FontStyleNormal}
2011-04-27 08:06:14 +00:00
type StackGraphicContext struct {
Current *ContextStack
}
type ContextStack struct {
2015-04-30 12:11:23 +00:00
Tr draw2d.Matrix
Path *draw2d.Path
2011-04-27 08:06:14 +00:00
LineWidth float64
Dash []float64
DashOffset float64
2012-01-13 09:14:12 +00:00
StrokeColor color.Color
FillColor color.Color
FillRule draw2d.FillRule
Cap draw2d.LineCap
Join draw2d.LineJoin
2011-04-27 08:06:14 +00:00
FontSize float64
FontData draw2d.FontData
Font *truetype.Font
// fontSize and dpi are used to calculate scale. scale is the number of
// 26.6 fixed point units in 1 em.
Scale float64
glyphBuf *truetype.GlyphBuf
DPI int
Previous *ContextStack
2011-04-27 08:06:14 +00:00
}
/**
* Create a new Graphic context from an image
*/
func NewStackGraphicContext() *StackGraphicContext {
gc := &StackGraphicContext{}
gc.Current = new(ContextStack)
gc.Current.Tr = draw2d.NewIdentityMatrix()
gc.Current.Path = new(draw2d.Path)
2011-04-27 08:06:14 +00:00
gc.Current.LineWidth = 1.0
gc.Current.StrokeColor = image.Black
gc.Current.FillColor = image.White
gc.Current.Cap = draw2d.RoundCap
gc.Current.FillRule = draw2d.FillRuleEvenOdd
gc.Current.Join = draw2d.RoundJoin
2011-04-27 08:06:14 +00:00
gc.Current.FontSize = 10
gc.Current.FontData = DefaultFontData
gc.Current.glyphBuf = &truetype.GlyphBuf{}
gc.Current.DPI = 92
2011-04-27 08:06:14 +00:00
return gc
}
2015-04-30 12:11:23 +00:00
func (gc *StackGraphicContext) GetMatrixTransform() draw2d.Matrix {
2011-04-27 08:06:14 +00:00
return gc.Current.Tr
}
2015-04-30 12:11:23 +00:00
func (gc *StackGraphicContext) SetMatrixTransform(Tr draw2d.Matrix) {
2011-04-27 08:06:14 +00:00
gc.Current.Tr = Tr
}
2015-04-30 12:11:23 +00:00
func (gc *StackGraphicContext) ComposeMatrixTransform(Tr draw2d.Matrix) {
gc.Current.Tr.Compose(Tr)
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) Rotate(angle float64) {
2015-04-30 12:11:23 +00:00
gc.Current.Tr.Rotate(angle)
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) Translate(tx, ty float64) {
2015-04-30 12:11:23 +00:00
gc.Current.Tr.Translate(tx, ty)
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) Scale(sx, sy float64) {
2015-04-30 12:11:23 +00:00
gc.Current.Tr.Scale(sx, sy)
2011-04-27 08:06:14 +00:00
}
2012-01-13 09:14:12 +00:00
func (gc *StackGraphicContext) SetStrokeColor(c color.Color) {
2011-04-27 08:06:14 +00:00
gc.Current.StrokeColor = c
}
2012-01-13 09:14:12 +00:00
func (gc *StackGraphicContext) SetFillColor(c color.Color) {
2011-04-27 08:06:14 +00:00
gc.Current.FillColor = c
}
func (gc *StackGraphicContext) SetFillRule(f draw2d.FillRule) {
2011-04-27 08:06:14 +00:00
gc.Current.FillRule = f
}
func (gc *StackGraphicContext) SetLineWidth(lineWidth float64) {
gc.Current.LineWidth = lineWidth
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) SetLineCap(cap draw2d.LineCap) {
gc.Current.Cap = cap
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) SetLineJoin(join draw2d.LineJoin) {
gc.Current.Join = join
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) SetLineDash(dash []float64, dashOffset float64) {
gc.Current.Dash = dash
gc.Current.DashOffset = dashOffset
2011-04-27 08:06:14 +00:00
}
// SetFontSize sets the font size in points (as in ``a 12 point font'').
func (gc *StackGraphicContext) SetFontSize(fontSize float64) {
gc.Current.FontSize = fontSize
gc.recalc()
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) GetFontSize() float64 {
return gc.Current.FontSize
}
func (gc *StackGraphicContext) SetFontData(fontData draw2d.FontData) {
gc.Current.FontData = fontData
2011-04-27 08:06:14 +00:00
}
func (gc *StackGraphicContext) GetFontData() draw2d.FontData {
2011-04-27 08:06:14 +00:00
return gc.Current.FontData
}
func (gc *StackGraphicContext) BeginPath() {
gc.Current.Path.Clear()
}
func (gc *StackGraphicContext) IsEmpty() bool {
return gc.Current.Path.IsEmpty()
}
func (gc *StackGraphicContext) LastPoint() (float64, float64) {
return gc.Current.Path.LastPoint()
}
func (gc *StackGraphicContext) MoveTo(x, y float64) {
gc.Current.Path.MoveTo(x, y)
}
func (gc *StackGraphicContext) LineTo(x, y float64) {
gc.Current.Path.LineTo(x, y)
}
func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) {
gc.Current.Path.QuadCurveTo(cx, cy, x, y)
}
func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y)
}
func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle)
}
func (gc *StackGraphicContext) Close() {
gc.Current.Path.Close()
}
func (gc *StackGraphicContext) Save() {
context := new(ContextStack)
context.FontSize = gc.Current.FontSize
context.FontData = gc.Current.FontData
context.LineWidth = gc.Current.LineWidth
context.StrokeColor = gc.Current.StrokeColor
context.FillColor = gc.Current.FillColor
context.FillRule = gc.Current.FillRule
context.Dash = gc.Current.Dash
context.DashOffset = gc.Current.DashOffset
context.Cap = gc.Current.Cap
context.Join = gc.Current.Join
context.Path = gc.Current.Path.Copy()
context.Font = gc.Current.Font
context.Scale = gc.Current.Scale
context.glyphBuf = gc.Current.glyphBuf
context.DPI = gc.Current.DPI
2011-04-27 08:06:14 +00:00
copy(context.Tr[:], gc.Current.Tr[:])
context.Previous = gc.Current
2011-04-27 08:06:14 +00:00
gc.Current = context
}
func (gc *StackGraphicContext) Restore() {
if gc.Current.Previous != nil {
2011-04-27 08:06:14 +00:00
oldContext := gc.Current
gc.Current = gc.Current.Previous
oldContext.Previous = nil
2011-04-27 08:06:14 +00:00
}
}
func (gc *StackGraphicContext) loadCurrentFont() (*truetype.Font, error) {
font := draw2d.GetFont(gc.Current.FontData)
if font == nil {
font = draw2d.GetFont(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 *StackGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
if err := gc.Current.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), glyph, font.HintingNone); err != nil {
return err
}
e0 := 0
for _, e1 := range gc.Current.glyphBuf.Ends {
DrawContour(gc, gc.Current.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 *StackGraphicContext) 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
}
// 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 *StackGraphicContext) 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.Current.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.Current.glyphBuf.Ends {
ps := gc.Current.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
}
// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (gc *StackGraphicContext) recalc() {
gc.Current.Scale = gc.Current.FontSize * float64(gc.Current.DPI) * (64.0 / 72.0)
}
func (gc *StackGraphicContext) SetDPI(dpi int) {
gc.Current.DPI = dpi
gc.recalc()
}
// SetFont sets the font used to draw text.
func (gc *StackGraphicContext) SetFont(font *truetype.Font) {
gc.Current.Font = font
}
func (gc *StackGraphicContext) GetDPI() int {
return gc.Current.DPI
}