draw2d/draw2dimg/ftgc.go

218 lines
6.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 draw2dimg
2011-04-27 08:06:14 +00:00
import (
"image"
2012-01-13 09:14:12 +00:00
"image/color"
2015-07-09 16:06:14 +00:00
"github.com/llgcode/draw2d"
"github.com/llgcode/draw2d/draw2dbase"
"github.com/golang/freetype/raster"
"golang.org/x/image/draw"
2016-01-02 19:05:47 +00:00
"golang.org/x/image/math/f64"
2011-04-27 08:06:14 +00:00
)
2015-07-09 16:06:14 +00:00
// Painter implements the freetype raster.Painter and has a SetColor method like the RGBAPainter
2011-04-27 08:06:14 +00:00
type Painter interface {
raster.Painter
2012-01-13 09:14:12 +00:00
SetColor(color color.Color)
2011-04-27 08:06:14 +00:00
}
2015-07-09 16:06:14 +00:00
// GraphicContext is the implementation of draw2d.GraphicContext for a raster image
type GraphicContext struct {
*draw2dbase.StackGraphicContext
2011-04-27 08:06:14 +00:00
img draw.Image
painter Painter
fillRasterizer *raster.Rasterizer
strokeRasterizer *raster.Rasterizer
}
// ImageFilter defines the type of filter to use
type ImageFilter int
const (
// LinearFilter defines a linear filter
LinearFilter ImageFilter = iota
// BilinearFilter defines a bilinear filter
BilinearFilter
// BicubicFilter defines a bicubic filter
BicubicFilter
)
2015-07-07 22:49:12 +00:00
// NewGraphicContext creates a new Graphic context from an image.
func NewGraphicContext(img draw.Image) *GraphicContext {
2015-07-09 16:06:14 +00:00
2011-04-27 08:06:14 +00:00
var painter Painter
switch selectImage := img.(type) {
case *image.RGBA:
painter = raster.NewRGBAPainter(selectImage)
default:
panic("Image type not supported")
}
return NewGraphicContextWithPainter(img, painter)
2011-04-27 08:06:14 +00:00
}
2015-07-07 22:49:12 +00:00
// NewGraphicContextWithPainter creates a new Graphic context from an image and a Painter (see Freetype-go)
func NewGraphicContextWithPainter(img draw.Image, painter Painter) *GraphicContext {
2012-05-28 07:52:49 +00:00
width, height := img.Bounds().Dx(), img.Bounds().Dy()
gc := &GraphicContext{
draw2dbase.NewStackGraphicContext(),
2012-05-28 07:52:49 +00:00
img,
painter,
raster.NewRasterizer(width, height),
raster.NewRasterizer(width, height),
}
return gc
}
2015-07-09 16:06:14 +00:00
// Clear fills the current canvas with a default transparent color
func (gc *GraphicContext) Clear() {
2011-04-27 08:06:14 +00:00
width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy()
gc.ClearRect(0, 0, width, height)
}
2015-07-09 16:06:14 +00:00
// ClearRect fills the current canvas with a default transparent color at the specified rectangle
func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
2012-01-13 09:14:12 +00:00
imageColor := image.NewUniform(gc.Current.FillColor)
2011-07-04 21:13:02 +00:00
draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over)
2011-04-27 08:06:14 +00:00
}
// DrawImage draws an image into dest using an affine transformation matrix, an op and a filter
func DrawImage(src image.Image, dest draw.Image, tr draw2d.Matrix, op draw.Op, filter ImageFilter) {
2016-01-02 19:05:47 +00:00
var transformer draw.Transformer
switch filter {
case LinearFilter:
2016-01-02 19:05:47 +00:00
transformer = draw.NearestNeighbor
case BilinearFilter:
2016-01-02 19:05:47 +00:00
transformer = draw.BiLinear
case BicubicFilter:
2016-01-02 19:05:47 +00:00
transformer = draw.CatmullRom
}
2016-06-27 19:46:37 +00:00
transformer.Transform(dest, f64.Aff3{tr[0], tr[1], tr[4], tr[2], tr[3], tr[5]}, src, src.Bounds(), op, nil)
}
2015-07-09 16:06:14 +00:00
// DrawImage draws the raster image in the current canvas
func (gc *GraphicContext) DrawImage(img image.Image) {
2011-04-27 08:06:14 +00:00
DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter)
}
2015-07-09 16:06:14 +00:00
// FillString draws the text at point (0, 0)
func (gc *GraphicContext) FillString(text string) (cursor float64) {
return gc.FillStringAt(text, 0, 0)
}
2015-07-09 16:06:14 +00:00
// FillStringAt draws the text at the specified point (x, y)
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
width := gc.CreateStringPath(text, x, y)
gc.Fill()
return width
}
2015-07-09 16:06:14 +00:00
// StrokeString draws the contour of the text at point (0, 0)
func (gc *GraphicContext) StrokeString(text string) (cursor float64) {
return gc.StrokeStringAt(text, 0, 0)
}
2015-07-09 16:06:14 +00:00
// StrokeStringAt draws the contour of the text at point (x, y)
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
width := gc.CreateStringPath(text, x, y)
gc.Stroke()
return width
}
func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
2011-04-27 08:06:14 +00:00
gc.painter.SetColor(color)
rasterizer.Rasterize(gc.painter)
rasterizer.Clear()
gc.Current.Path.Clear()
}
2015-07-07 22:49:12 +00:00
// Stroke strokes the paths with the color specified by SetStrokeColor
func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
2011-04-27 08:06:14 +00:00
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
2015-08-14 20:22:01 +00:00
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: FtLineBuilder{Adder: gc.strokeRasterizer}})
2011-04-27 08:06:14 +00:00
stroker.HalfLineWidth = gc.Current.LineWidth / 2
2015-04-29 15:16:15 +00:00
var liner draw2dbase.Flattener
2011-04-27 08:06:14 +00:00
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
2011-04-27 08:06:14 +00:00
} else {
liner = stroker
}
for _, p := range paths {
2015-04-29 15:16:15 +00:00
draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
2011-04-27 08:06:14 +00:00
}
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}
2015-07-07 22:49:12 +00:00
// Fill fills the paths with the color specified by SetFillColor
func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
2011-04-27 08:06:14 +00:00
paths = append(paths, gc.Current.Path)
2015-07-09 16:06:14 +00:00
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule == draw2d.FillRuleWinding
2011-04-27 08:06:14 +00:00
/**** first method ****/
2015-08-14 20:22:01 +00:00
flattener := draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: FtLineBuilder{Adder: gc.fillRasterizer}}
for _, p := range paths {
2015-04-29 15:16:15 +00:00
draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
}
2011-04-27 08:06:14 +00:00
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
}
2015-07-07 22:49:12 +00:00
// FillStroke first fills the paths and than strokes them
func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
paths = append(paths, gc.Current.Path)
2015-07-09 16:06:14 +00:00
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule == draw2d.FillRuleWinding
2011-04-27 08:06:14 +00:00
gc.strokeRasterizer.UseNonZeroWinding = true
2015-08-14 20:22:01 +00:00
flattener := draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: FtLineBuilder{Adder: gc.fillRasterizer}}
2011-04-27 08:06:14 +00:00
2015-08-14 20:22:01 +00:00
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{Tr: gc.Current.Tr, Flattener: FtLineBuilder{Adder: gc.strokeRasterizer}})
2011-04-27 08:06:14 +00:00
stroker.HalfLineWidth = gc.Current.LineWidth / 2
2015-04-29 15:16:15 +00:00
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
}
2011-04-27 08:06:14 +00:00
2015-07-09 16:06:14 +00:00
demux := draw2dbase.DemuxFlattener{Flatteners: []draw2dbase.Flattener{flattener, liner}}
for _, p := range paths {
2015-04-29 15:16:15 +00:00
draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
}
// Fill
2011-04-27 08:06:14 +00:00
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
// Stroke
2011-04-27 08:06:14 +00:00
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}
2015-08-14 20:22:01 +00:00
func toFtCap(c draw2d.LineCap) raster.Capper {
switch c {
case draw2d.RoundCap:
return raster.RoundCapper
case draw2d.ButtCap:
return raster.ButtCapper
case draw2d.SquareCap:
return raster.SquareCapper
}
return raster.RoundCapper
}
func toFtJoin(j draw2d.LineJoin) raster.Joiner {
switch j {
case draw2d.RoundJoin:
return raster.RoundJoiner
case draw2d.BevelJoin:
return raster.BevelJoiner
}
return raster.RoundJoiner
}