aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/fogleman
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2018-10-04 19:44:06 -0700
committerChristine Dodrill <me@christine.website>2018-10-04 19:44:06 -0700
commit1c1d089725dd9f98b7ac73276d07dbadb388b748 (patch)
treeb0e783f5e2d485050e0072faf3b303bdc66e3539 /vendor/github.com/fogleman
parente36f755db2198a96e2b87781f5f5432fcee092dd (diff)
downloadx-1c1d089725dd9f98b7ac73276d07dbadb388b748.tar.xz
x-1c1d089725dd9f98b7ac73276d07dbadb388b748.zip
add Dockerfile
Diffstat (limited to 'vendor/github.com/fogleman')
-rw-r--r--vendor/github.com/fogleman/gg/.gitignore2
-rw-r--r--vendor/github.com/fogleman/gg/LICENSE.md19
-rw-r--r--vendor/github.com/fogleman/gg/README.md216
-rw-r--r--vendor/github.com/fogleman/gg/bezier.go59
-rw-r--r--vendor/github.com/fogleman/gg/context.go824
-rw-r--r--vendor/github.com/fogleman/gg/gradient.go202
-rw-r--r--vendor/github.com/fogleman/gg/matrix.go88
-rw-r--r--vendor/github.com/fogleman/gg/path.go140
-rw-r--r--vendor/github.com/fogleman/gg/pattern.go123
-rw-r--r--vendor/github.com/fogleman/gg/point.go25
-rw-r--r--vendor/github.com/fogleman/gg/util.go118
-rw-r--r--vendor/github.com/fogleman/gg/wrap.go58
-rw-r--r--vendor/github.com/fogleman/primitive/LICENSE.md19
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/color.go44
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/core.go124
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/ellipse.go179
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/heatmap.go59
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/log.go23
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/model.go174
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/optimize.go75
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/polygon.go122
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/quadratic.go100
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/raster.go46
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/rectangle.go198
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/scanline.go29
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/shape.go25
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/state.go48
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/triangle.go171
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/util.go195
-rw-r--r--vendor/github.com/fogleman/primitive/primitive/worker.go108
30 files changed, 3613 insertions, 0 deletions
diff --git a/vendor/github.com/fogleman/gg/.gitignore b/vendor/github.com/fogleman/gg/.gitignore
new file mode 100644
index 0000000..2fa80d6
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/.gitignore
@@ -0,0 +1,2 @@
+*.png
+
diff --git a/vendor/github.com/fogleman/gg/LICENSE.md b/vendor/github.com/fogleman/gg/LICENSE.md
new file mode 100644
index 0000000..d7b4099
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/LICENSE.md
@@ -0,0 +1,19 @@
+Copyright (C) 2016 Michael Fogleman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/fogleman/gg/README.md b/vendor/github.com/fogleman/gg/README.md
new file mode 100644
index 0000000..0417f94
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/README.md
@@ -0,0 +1,216 @@
+# Go Graphics
+
+`gg` is a library for rendering 2D graphics in pure Go.
+
+![Stars](http://i.imgur.com/CylQIJt.png)
+
+## Installation
+
+ go get -u github.com/fogleman/gg
+
+Alternatively, you may use gopkg.in to grab a specific major-version:
+
+ go get -u gopkg.in/fogleman/gg.v1
+
+## Documentation
+
+https://godoc.org/github.com/fogleman/gg
+
+## Hello, Circle!
+
+Look how easy!
+
+```go
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ dc := gg.NewContext(1000, 1000)
+ dc.DrawCircle(500, 500, 400)
+ dc.SetRGB(0, 0, 0)
+ dc.Fill()
+ dc.SavePNG("out.png")
+}
+```
+
+## Examples
+
+There are [lots of examples](https://github.com/fogleman/gg/tree/master/examples) included. They're mostly for testing the code, but they're good for learning, too.
+
+![Examples](http://i.imgur.com/tMFoyzu.png)
+
+## Creating Contexts
+
+There are a few ways of creating a context.
+
+```go
+NewContext(width, height int) *Context
+NewContextForImage(im image.Image) *Context
+NewContextForRGBA(im *image.RGBA) *Context
+```
+
+## Drawing Functions
+
+Ever used a graphics library that didn't have functions for drawing rectangles
+or circles? What a pain!
+
+```go
+DrawPoint(x, y, r float64)
+DrawLine(x1, y1, x2, y2 float64)
+DrawRectangle(x, y, w, h float64)
+DrawRoundedRectangle(x, y, w, h, r float64)
+DrawCircle(x, y, r float64)
+DrawArc(x, y, r, angle1, angle2 float64)
+DrawEllipse(x, y, rx, ry float64)
+DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64)
+DrawRegularPolygon(n int, x, y, r, rotation float64)
+DrawImage(im image.Image, x, y int)
+DrawImageAnchored(im image.Image, x, y int, ax, ay float64)
+SetPixel(x, y int)
+
+MoveTo(x, y float64)
+LineTo(x, y float64)
+QuadraticTo(x1, y1, x2, y2 float64)
+CubicTo(x1, y1, x2, y2, x3, y3 float64)
+ClosePath()
+ClearPath()
+NewSubPath()
+
+Clear()
+Stroke()
+Fill()
+StrokePreserve()
+FillPreserve()
+```
+
+It is often desired to center an image at a point. Use `DrawImageAnchored` with `ax` and `ay` set to 0.5 to do this. Use 0 to left or top align. Use 1 to right or bottom align. `DrawStringAnchored` does the same for text, so you don't need to call `MeasureString` yourself.
+
+## Text Functions
+
+It will even do word wrap for you!
+
+```go
+DrawString(s string, x, y float64)
+DrawStringAnchored(s string, x, y, ax, ay float64)
+DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align)
+MeasureString(s string) (w, h float64)
+WordWrap(s string, w float64) []string
+SetFontFace(fontFace font.Face)
+LoadFontFace(path string, points float64) error
+```
+
+## Color Functions
+
+Colors can be set in several different ways for your convenience.
+
+```go
+SetRGB(r, g, b float64)
+SetRGBA(r, g, b, a float64)
+SetRGB255(r, g, b int)
+SetRGBA255(r, g, b, a int)
+SetColor(c color.Color)
+SetHexColor(x string)
+```
+
+## Stroke & Fill Options
+
+```go
+SetLineWidth(lineWidth float64)
+SetLineCap(lineCap LineCap)
+SetLineJoin(lineJoin LineJoin)
+SetDash(dashes ...float64)
+SetFillRule(fillRule FillRule)
+```
+
+## Gradients & Patterns
+
+`gg` supports linear and radial gradients and surface patterns. You can also implement your own patterns.
+
+```go
+SetFillStyle(pattern Pattern)
+SetStrokeStyle(pattern Pattern)
+NewSolidPattern(color color.Color)
+NewLinearGradient(x0, y0, x1, y1 float64)
+NewRadialGradient(x0, y0, r0, x1, y1, r1 float64)
+NewSurfacePattern(im image.Image, op RepeatOp)
+```
+
+## Transformation Functions
+
+```go
+Identity()
+Translate(x, y float64)
+Scale(x, y float64)
+Rotate(angle float64)
+Shear(x, y float64)
+ScaleAbout(sx, sy, x, y float64)
+RotateAbout(angle, x, y float64)
+ShearAbout(sx, sy, x, y float64)
+TransformPoint(x, y float64) (tx, ty float64)
+InvertY()
+```
+
+It is often desired to rotate or scale about a point that is not the origin. The functions `RotateAbout`, `ScaleAbout`, `ShearAbout` are provided as a convenience.
+
+`InvertY` is provided in case Y should increase from bottom to top vs. the default top to bottom.
+
+## Stack Functions
+
+Save and restore the state of the context. These can be nested.
+
+```go
+Push()
+Pop()
+```
+
+## Clipping Functions
+
+Use clipping regions to restrict drawing operations to an area that you
+defined using paths.
+
+```go
+Clip()
+ClipPreserve()
+ResetClip()
+```
+
+## Helper Functions
+
+Sometimes you just don't want to write these yourself.
+
+```go
+Radians(degrees float64) float64
+Degrees(radians float64) float64
+LoadImage(path string) (image.Image, error)
+LoadPNG(path string) (image.Image, error)
+SavePNG(path string, im image.Image) error
+```
+
+![Separator](http://i.imgur.com/fsUvnPB.png)
+
+## Another Example
+
+See the output of this example below.
+
+```go
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1024
+ dc := gg.NewContext(S, S)
+ dc.SetRGBA(0, 0, 0, 0.1)
+ for i := 0; i < 360; i += 15 {
+ dc.Push()
+ dc.RotateAbout(gg.Radians(float64(i)), S/2, S/2)
+ dc.DrawEllipse(S/2, S/2, S*7/16, S/8)
+ dc.Fill()
+ dc.Pop()
+ }
+ dc.SavePNG("out.png")
+}
+```
+
+![Ellipses](http://i.imgur.com/J9CBZef.png)
diff --git a/vendor/github.com/fogleman/gg/bezier.go b/vendor/github.com/fogleman/gg/bezier.go
new file mode 100644
index 0000000..f2cd7ab
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/bezier.go
@@ -0,0 +1,59 @@
+package gg
+
+import "math"
+
+func quadratic(x0, y0, x1, y1, x2, y2, t float64) (x, y float64) {
+ u := 1 - t
+ a := u * u
+ b := 2 * u * t
+ c := t * t
+ x = a*x0 + b*x1 + c*x2
+ y = a*y0 + b*y1 + c*y2
+ return
+}
+
+func QuadraticBezier(x0, y0, x1, y1, x2, y2 float64) []Point {
+ l := (math.Hypot(x1-x0, y1-y0) +
+ math.Hypot(x2-x1, y2-y1))
+ n := int(l + 0.5)
+ if n < 4 {
+ n = 4
+ }
+ d := float64(n) - 1
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ t := float64(i) / d
+ x, y := quadratic(x0, y0, x1, y1, x2, y2, t)
+ result[i] = Point{x, y}
+ }
+ return result
+}
+
+func cubic(x0, y0, x1, y1, x2, y2, x3, y3, t float64) (x, y float64) {
+ u := 1 - t
+ a := u * u * u
+ b := 3 * u * u * t
+ c := 3 * u * t * t
+ d := t * t * t
+ x = a*x0 + b*x1 + c*x2 + d*x3
+ y = a*y0 + b*y1 + c*y2 + d*y3
+ return
+}
+
+func CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) []Point {
+ l := (math.Hypot(x1-x0, y1-y0) +
+ math.Hypot(x2-x1, y2-y1) +
+ math.Hypot(x3-x2, y3-y2))
+ n := int(l + 0.5)
+ if n < 4 {
+ n = 4
+ }
+ d := float64(n) - 1
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ t := float64(i) / d
+ x, y := cubic(x0, y0, x1, y1, x2, y2, x3, y3, t)
+ result[i] = Point{x, y}
+ }
+ return result
+}
diff --git a/vendor/github.com/fogleman/gg/context.go b/vendor/github.com/fogleman/gg/context.go
new file mode 100644
index 0000000..3d973b8
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/context.go
@@ -0,0 +1,824 @@
+// Package gg provides a simple API for rendering 2D graphics in pure Go.
+package gg
+
+import (
+ "errors"
+ "image"
+ "image/color"
+ "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
+
+const (
+ LineCapRound LineCap = iota
+ LineCapButt
+ LineCapSquare
+)
+
+type LineJoin int
+
+const (
+ LineJoinRound LineJoin = iota
+ LineJoinBevel
+)
+
+type FillRule int
+
+const (
+ FillRuleWinding FillRule = iota
+ FillRuleEvenOdd
+)
+
+type Align int
+
+const (
+ AlignLeft Align = iota
+ AlignCenter
+ AlignRight
+)
+
+var (
+ defaultFillStyle = NewSolidPattern(color.White)
+ defaultStrokeStyle = NewSolidPattern(color.Black)
+)
+
+type Context struct {
+ width int
+ height int
+ im *image.RGBA
+ mask *image.Alpha
+ color color.Color
+ fillPattern Pattern
+ strokePattern Pattern
+ strokePath raster.Path
+ fillPath raster.Path
+ start Point
+ current Point
+ hasCurrent bool
+ dashes []float64
+ lineWidth float64
+ lineCap LineCap
+ lineJoin LineJoin
+ fillRule FillRule
+ fontFace font.Face
+ fontHeight float64
+ matrix Matrix
+ stack []*Context
+}
+
+// NewContext creates a new image.RGBA with the specified width and height
+// and prepares a context for rendering onto that image.
+func NewContext(width, height int) *Context {
+ return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height)))
+}
+
+// NewContextForImage copies the specified image into a new image.RGBA
+// and prepares a context for rendering onto that image.
+func NewContextForImage(im image.Image) *Context {
+ return NewContextForRGBA(imageToRGBA(im))
+}
+
+// NewContextForRGBA prepares a context for rendering onto the specified image.
+// No copy is made.
+func NewContextForRGBA(im *image.RGBA) *Context {
+ return &Context{
+ width: im.Bounds().Size().X,
+ height: im.Bounds().Size().Y,
+ im: im,
+ color: color.Transparent,
+ fillPattern: defaultFillStyle,
+ strokePattern: defaultStrokeStyle,
+ lineWidth: 1,
+ fillRule: FillRuleWinding,
+ fontFace: basicfont.Face7x13,
+ fontHeight: 13,
+ matrix: Identity(),
+ }
+}
+
+// Image returns the image that has been drawn by this context.
+func (dc *Context) Image() image.Image {
+ return dc.im
+}
+
+// Width returns the width of the image in pixels.
+func (dc *Context) Width() int {
+ return dc.width
+}
+
+// Height returns the height of the image in pixels.
+func (dc *Context) Height() int {
+ return dc.height
+}
+
+// SavePNG encodes the image as a PNG and writes it to disk.
+func (dc *Context) SavePNG(path string) error {
+ return SavePNG(path, dc.im)
+}
+
+// EncodePNG encodes the image as a PNG and writes it to the provided io.Writer.
+func (dc *Context) EncodePNG(w io.Writer) error {
+ return png.Encode(w, dc.im)
+}
+
+// SetDash sets the current dash pattern to use. Call with zero arguments to
+// disable dashes. The values specify the lengths of each dash, with
+// alternating on and off lengths.
+func (dc *Context) SetDash(dashes ...float64) {
+ dc.dashes = dashes
+}
+
+func (dc *Context) SetLineWidth(lineWidth float64) {
+ dc.lineWidth = lineWidth
+}
+
+func (dc *Context) SetLineCap(lineCap LineCap) {
+ dc.lineCap = lineCap
+}
+
+func (dc *Context) SetLineCapRound() {
+ dc.lineCap = LineCapRound
+}
+
+func (dc *Context) SetLineCapButt() {
+ dc.lineCap = LineCapButt
+}
+
+func (dc *Context) SetLineCapSquare() {
+ dc.lineCap = LineCapSquare
+}
+
+func (dc *Context) SetLineJoin(lineJoin LineJoin) {
+ dc.lineJoin = lineJoin
+}
+
+func (dc *Context) SetLineJoinRound() {
+ dc.lineJoin = LineJoinRound
+}
+
+func (dc *Context) SetLineJoinBevel() {
+ dc.lineJoin = LineJoinBevel
+}
+
+func (dc *Context) SetFillRule(fillRule FillRule) {
+ dc.fillRule = fillRule
+}
+
+func (dc *Context) SetFillRuleWinding() {
+ dc.fillRule = FillRuleWinding
+}
+
+func (dc *Context) SetFillRuleEvenOdd() {
+ dc.fillRule = FillRuleEvenOdd
+}
+
+// Color Setters
+
+func (dc *Context) setFillAndStrokeColor(c color.Color) {
+ dc.color = c
+ dc.fillPattern = NewSolidPattern(c)
+ dc.strokePattern = NewSolidPattern(c)
+}
+
+// SetFillStyle sets current fill style
+func (dc *Context) SetFillStyle(pattern Pattern) {
+ // if pattern is SolidPattern, also change dc.color(for dc.Clear, dc.drawString)
+ if fillStyle, ok := pattern.(*solidPattern); ok {
+ dc.color = fillStyle.color
+ }
+ dc.fillPattern = pattern
+}
+
+// SetStrokeStyle sets current stroke style
+func (dc *Context) SetStrokeStyle(pattern Pattern) {
+ dc.strokePattern = pattern
+}
+
+// SetColor sets the current color(for both fill and stroke).
+func (dc *Context) SetColor(c color.Color) {
+ dc.setFillAndStrokeColor(c)
+}
+
+// SetHexColor sets the current color using a hex string. The leading pound
+// sign (#) is optional. Both 3- and 6-digit variations are supported. 8 digits
+// may be provided to set the alpha value as well.
+func (dc *Context) SetHexColor(x string) {
+ r, g, b, a := parseHexColor(x)
+ dc.SetRGBA255(r, g, b, a)
+}
+
+// SetRGBA255 sets the current color. r, g, b, a values should be between 0 and
+// 255, inclusive.
+func (dc *Context) SetRGBA255(r, g, b, a int) {
+ dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
+ dc.setFillAndStrokeColor(dc.color)
+}
+
+// SetRGB255 sets the current color. r, g, b values should be between 0 and 255,
+// inclusive. Alpha will be set to 255 (fully opaque).
+func (dc *Context) SetRGB255(r, g, b int) {
+ dc.SetRGBA255(r, g, b, 255)
+}
+
+// SetRGBA sets the current color. r, g, b, a values should be between 0 and 1,
+// inclusive.
+func (dc *Context) SetRGBA(r, g, b, a float64) {
+ dc.color = color.NRGBA{
+ uint8(r * 255),
+ uint8(g * 255),
+ uint8(b * 255),
+ uint8(a * 255),
+ }
+ dc.setFillAndStrokeColor(dc.color)
+}
+
+// SetRGB sets the current color. r, g, b values should be between 0 and 1,
+// inclusive. Alpha will be set to 1 (fully opaque).
+func (dc *Context) SetRGB(r, g, b float64) {
+ dc.SetRGBA(r, g, b, 1)
+}
+
+// Path Manipulation
+
+// MoveTo starts a new subpath within the current path starting at the
+// specified point.
+func (dc *Context) MoveTo(x, y float64) {
+ if dc.hasCurrent {
+ dc.fillPath.Add1(dc.start.Fixed())
+ }
+ x, y = dc.TransformPoint(x, y)
+ p := Point{x, y}
+ dc.strokePath.Start(p.Fixed())
+ dc.fillPath.Start(p.Fixed())
+ dc.start = p
+ dc.current = p
+ dc.hasCurrent = true
+}
+
+// LineTo adds a line segment to the current path starting at the current
+// point. If there is no current point, it is equivalent to MoveTo(x, y)
+func (dc *Context) LineTo(x, y float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x, y)
+ } else {
+ x, y = dc.TransformPoint(x, y)
+ p := Point{x, y}
+ dc.strokePath.Add1(p.Fixed())
+ dc.fillPath.Add1(p.Fixed())
+ dc.current = p
+ }
+}
+
+// QuadraticTo adds a quadratic bezier curve to the current path starting at
+// the current point. If there is no current point, it first performs
+// MoveTo(x1, y1)
+func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x1, y1)
+ }
+ x1, y1 = dc.TransformPoint(x1, y1)
+ x2, y2 = dc.TransformPoint(x2, y2)
+ p1 := Point{x1, y1}
+ p2 := Point{x2, y2}
+ dc.strokePath.Add2(p1.Fixed(), p2.Fixed())
+ dc.fillPath.Add2(p1.Fixed(), p2.Fixed())
+ dc.current = p2
+}
+
+// CubicTo adds a cubic bezier curve to the current path starting at the
+// current point. If there is no current point, it first performs
+// MoveTo(x1, y1). Because freetype/raster does not support cubic beziers,
+// this is emulated with many small line segments.
+func (dc *Context) CubicTo(x1, y1, x2, y2, x3, y3 float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x1, y1)
+ }
+ x0, y0 := dc.current.X, dc.current.Y
+ x1, y1 = dc.TransformPoint(x1, y1)
+ x2, y2 = dc.TransformPoint(x2, y2)
+ x3, y3 = dc.TransformPoint(x3, y3)
+ points := CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3)
+ previous := dc.current.Fixed()
+ for _, p := range points[1:] {
+ f := p.Fixed()
+ if f == previous {
+ // TODO: this fixes some rendering issues but not all
+ continue
+ }
+ previous = f
+ dc.strokePath.Add1(f)
+ dc.fillPath.Add1(f)
+ dc.current = p
+ }
+}
+
+// ClosePath adds a line segment from the current point to the beginning
+// of the current subpath. If there is no current point, this is a no-op.
+func (dc *Context) ClosePath() {
+ if dc.hasCurrent {
+ dc.strokePath.Add1(dc.start.Fixed())
+ dc.fillPath.Add1(dc.start.Fixed())
+ dc.current = dc.start
+ }
+}
+
+// ClearPath clears the current path. There is no current point after this
+// operation.
+func (dc *Context) ClearPath() {
+ dc.strokePath.Clear()
+ dc.fillPath.Clear()
+ dc.hasCurrent = false
+}
+
+// NewSubPath starts a new subpath within the current path. There is no current
+// point after this operation.
+func (dc *Context) NewSubPath() {
+ if dc.hasCurrent {
+ dc.fillPath.Add1(dc.start.Fixed())
+ }
+ dc.hasCurrent = false
+}
+
+// Path Drawing
+
+func (dc *Context) capper() raster.Capper {
+ switch dc.lineCap {
+ case LineCapButt:
+ return raster.ButtCapper
+ case LineCapRound:
+ return raster.RoundCapper
+ case LineCapSquare:
+ return raster.SquareCapper
+ }
+ return nil
+}
+
+func (dc *Context) joiner() raster.Joiner {
+ switch dc.lineJoin {
+ case LineJoinBevel:
+ return raster.BevelJoiner
+ case LineJoinRound:
+ return raster.RoundJoiner
+ }
+ return nil
+}
+
+func (dc *Context) stroke(painter raster.Painter) {
+ path := dc.strokePath
+ if len(dc.dashes) > 0 {
+ path = dashed(path, dc.dashes)
+ } else {
+ // TODO: this is a temporary workaround to remove tiny segments
+ // that result in rendering issues
+ path = rasterPath(flattenPath(path))
+ }
+ r := raster.NewRasterizer(dc.width, dc.height)
+ r.UseNonZeroWinding = true
+ r.AddStroke(path, fix(dc.lineWidth), dc.capper(), dc.joiner())
+ r.Rasterize(painter)
+}
+
+func (dc *Context) fill(painter raster.Painter) {
+ path := dc.fillPath
+ if dc.hasCurrent {
+ path = make(raster.Path, len(dc.fillPath))
+ copy(path, dc.fillPath)
+ path.Add1(dc.start.Fixed())
+ }
+ r := raster.NewRasterizer(dc.width, dc.height)
+ r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
+ r.AddPath(path)
+ r.Rasterize(painter)
+}
+
+// StrokePreserve strokes the current path with the current color, line width,
+// line cap, line join and dash settings. The path is preserved after this
+// operation.
+func (dc *Context) StrokePreserve() {
+ painter := newPatternPainter(dc.im, dc.mask, dc.strokePattern)
+ dc.stroke(painter)
+}
+
+// Stroke strokes the current path with the current color, line width,
+// line cap, line join and dash settings. The path is cleared after this
+// operation.
+func (dc *Context) Stroke() {
+ dc.StrokePreserve()
+ dc.ClearPath()
+}
+
+// FillPreserve fills the current path with the current color. Open subpaths
+// are implicity closed. The path is preserved after this operation.
+func (dc *Context) FillPreserve() {
+ painter := newPatternPainter(dc.im, dc.mask, dc.fillPattern)
+ dc.fill(painter)
+}
+
+// Fill fills the current path with the current color. Open subpaths
+// are implicity closed. The path is cleared after this operation.
+func (dc *Context) Fill() {
+ dc.FillPreserve()
+ dc.ClearPath()
+}
+
+// ClipPreserve updates the clipping region by intersecting the current
+// clipping region with the current path as it would be filled by dc.Fill().
+// The path is preserved after this operation.
+func (dc *Context) ClipPreserve() {
+ clip := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
+ painter := raster.NewAlphaOverPainter(clip)
+ dc.fill(painter)
+ if dc.mask == nil {
+ dc.mask = clip
+ } else {
+ mask := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
+ draw.DrawMask(mask, mask.Bounds(), clip, image.ZP, dc.mask, image.ZP, draw.Over)
+ dc.mask = mask
+ }
+}
+
+// SetMask allows you to directly set the *image.Alpha to be used as a clipping
+// mask. It must be the same size as the context, else an error is returned
+// and the mask is unchanged.
+func (dc *Context) SetMask(mask *image.Alpha) error {
+ if mask.Bounds().Size() != dc.im.Bounds().Size() {
+ return errors.New("mask size must match context size")
+ }
+ dc.mask = mask
+ return nil
+}
+
+// AsMask returns an *image.Alpha representing the alpha channel of this
+// context. This can be useful for advanced clipping operations where you first
+// render the mask geometry and then use it as a mask.
+func (dc *Context) AsMask() *image.Alpha {
+ mask := image.NewAlpha(dc.im.Bounds())
+ draw.Draw(mask, dc.im.Bounds(), dc.im, image.ZP, draw.Src)
+ return mask
+}
+
+// Clip updates the clipping region by intersecting the current
+// clipping region with the current path as it would be filled by dc.Fill().
+// The path is cleared after this operation.
+func (dc *Context) Clip() {
+ dc.ClipPreserve()
+ dc.ClearPath()
+}
+
+// ResetClip clears the clipping region.
+func (dc *Context) ResetClip() {
+ dc.mask = nil
+}
+
+// Convenient Drawing Functions
+
+// Clear fills the entire image with the current color.
+func (dc *Context) Clear() {
+ src := image.NewUniform(dc.color)
+ draw.Draw(dc.im, dc.im.Bounds(), src, image.ZP, draw.Src)
+}
+
+// SetPixel sets the color of the specified pixel using the current color.
+func (dc *Context) SetPixel(x, y int) {
+ dc.im.Set(x, y, dc.color)
+}
+
+// DrawPoint is like DrawCircle but ensures that a circle of the specified
+// size is drawn regardless of the current transformation matrix. The position
+// is still transformed, but not the shape of the point.
+func (dc *Context) DrawPoint(x, y, r float64) {
+ dc.Push()
+ tx, ty := dc.TransformPoint(x, y)
+ dc.Identity()
+ dc.DrawCircle(tx, ty, r)
+ dc.Pop()
+}
+
+func (dc *Context) DrawLine(x1, y1, x2, y2 float64) {
+ dc.MoveTo(x1, y1)
+ dc.LineTo(x2, y2)
+}
+
+func (dc *Context) DrawRectangle(x, y, w, h float64) {
+ dc.NewSubPath()
+ dc.MoveTo(x, y)
+ dc.LineTo(x+w, y)
+ dc.LineTo(x+w, y+h)
+ dc.LineTo(x, y+h)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawRoundedRectangle(x, y, w, h, r float64) {
+ x0, x1, x2, x3 := x, x+r, x+w-r, x+w
+ y0, y1, y2, y3 := y, y+r, y+h-r, y+h
+ dc.NewSubPath()
+ dc.MoveTo(x1, y0)
+ dc.LineTo(x2, y0)
+ dc.DrawArc(x2, y1, r, Radians(270), Radians(360))
+ dc.LineTo(x3, y2)
+ dc.DrawArc(x2, y2, r, Radians(0), Radians(90))
+ dc.LineTo(x1, y3)
+ dc.DrawArc(x1, y2, r, Radians(90), Radians(180))
+ dc.LineTo(x0, y1)
+ dc.DrawArc(x1, y1, r, Radians(180), Radians(270))
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) {
+ const n = 16
+ for i := 0; i < n; i++ {
+ p1 := float64(i+0) / n
+ p2 := float64(i+1) / n
+ a1 := angle1 + (angle2-angle1)*p1
+ a2 := angle1 + (angle2-angle1)*p2
+ x0 := x + rx*math.Cos(a1)
+ y0 := y + ry*math.Sin(a1)
+ x1 := x + rx*math.Cos(a1+(a2-a1)/2)
+ y1 := y + ry*math.Sin(a1+(a2-a1)/2)
+ x2 := x + rx*math.Cos(a2)
+ y2 := y + ry*math.Sin(a2)
+ cx := 2*x1 - x0/2 - x2/2
+ cy := 2*y1 - y0/2 - y2/2
+ if i == 0 {
+ if dc.hasCurrent {
+ dc.LineTo(x0, y0)
+ } else {
+ dc.MoveTo(x0, y0)
+ }
+ }
+ dc.QuadraticTo(cx, cy, x2, y2)
+ }
+}
+
+func (dc *Context) DrawEllipse(x, y, rx, ry float64) {
+ dc.NewSubPath()
+ dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) {
+ dc.DrawEllipticalArc(x, y, r, r, angle1, angle2)
+}
+
+func (dc *Context) DrawCircle(x, y, r float64) {
+ dc.NewSubPath()
+ dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawRegularPolygon(n int, x, y, r, rotation float64) {
+ angle := 2 * math.Pi / float64(n)
+ rotation -= math.Pi / 2
+ if n%2 == 0 {
+ rotation += angle / 2
+ }
+ dc.NewSubPath()
+ for i := 0; i < n; i++ {
+ a := rotation + angle*float64(i)
+ dc.LineTo(x+r*math.Cos(a), y+r*math.Sin(a))
+ }
+ dc.ClosePath()
+}
+
+// DrawImage draws the specified image at the specified point.
+func (dc *Context) DrawImage(im image.Image, x, y int) {
+ dc.DrawImageAnchored(im, x, y, 0, 0)
+}
+
+// DrawImageAnchored draws the specified image at the