diff options
| author | Christine Dodrill <me@christine.website> | 2018-10-04 19:44:06 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-10-04 19:44:06 -0700 |
| commit | 1c1d089725dd9f98b7ac73276d07dbadb388b748 (patch) | |
| tree | b0e783f5e2d485050e0072faf3b303bdc66e3539 /vendor/github.com/fogleman | |
| parent | e36f755db2198a96e2b87781f5f5432fcee092dd (diff) | |
| download | x-1c1d089725dd9f98b7ac73276d07dbadb388b748.tar.xz x-1c1d089725dd9f98b7ac73276d07dbadb388b748.zip | |
add Dockerfile
Diffstat (limited to 'vendor/github.com/fogleman')
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. + + + +## 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. + + + +## 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 +``` + + + +## 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") +} +``` + + 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 |
