diff options
Diffstat (limited to 'vendor/golang.org/x/image/vector/vector.go')
-rw-r--r-- | vendor/golang.org/x/image/vector/vector.go | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/vendor/golang.org/x/image/vector/vector.go b/vendor/golang.org/x/image/vector/vector.go new file mode 100644 index 000000000..218e76489 --- /dev/null +++ b/vendor/golang.org/x/image/vector/vector.go @@ -0,0 +1,228 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vector provides a rasterizer for 2-D vector graphics. +package vector // import "golang.org/x/image/vector" + +// The rasterizer's design follows +// https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445 +// +// Proof of concept code is in +// https://github.com/google/font-go +// +// See also: +// http://nothings.org/gamedev/rasterize/ +// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm +// https://people.gnome.org/~mathieu/libart/internals.html#INTERNALS-SCANLINE + +import ( + "image" + "image/draw" + "math" + + "golang.org/x/image/math/f32" +) + +func midPoint(p, q f32.Vec2) f32.Vec2 { + return f32.Vec2{ + (p[0] + q[0]) * 0.5, + (p[1] + q[1]) * 0.5, + } +} + +func lerp(t float32, p, q f32.Vec2) f32.Vec2 { + return f32.Vec2{ + p[0] + t*(q[0]-p[0]), + p[1] + t*(q[1]-p[1]), + } +} + +func clamp(i, width int32) uint { + if i < 0 { + return 0 + } + if i < width { + return uint(i) + } + return uint(width) +} + +// NewRasterizer returns a new Rasterizer whose rendered mask image is bounded +// by the given width and height. +func NewRasterizer(w, h int) *Rasterizer { + return &Rasterizer{ + area: make([]float32, w*h), + size: image.Point{w, h}, + } +} + +// Raster is a 2-D vector graphics rasterizer. +type Rasterizer struct { + area []float32 + size image.Point + first f32.Vec2 + pen f32.Vec2 + + // DrawOp is the operator used for the Draw method. + // + // The zero value is draw.Over. + DrawOp draw.Op + + // TODO: an exported field equivalent to the mask point in the + // draw.DrawMask function in the stdlib image/draw package? +} + +// Reset resets a Rasterizer as if it was just returned by NewRasterizer. +// +// This includes setting z.DrawOp to draw.Over. +func (z *Rasterizer) Reset(w, h int) { + if n := w * h; n > cap(z.area) { + z.area = make([]float32, n) + } else { + z.area = z.area[:n] + for i := range z.area { + z.area[i] = 0 + } + } + z.size = image.Point{w, h} + z.first = f32.Vec2{} + z.pen = f32.Vec2{} + z.DrawOp = draw.Over +} + +// Size returns the width and height passed to NewRasterizer or Reset. +func (z *Rasterizer) Size() image.Point { + return z.size +} + +// Bounds returns the rectangle from (0, 0) to the width and height passed to +// NewRasterizer or Reset. +func (z *Rasterizer) Bounds() image.Rectangle { + return image.Rectangle{Max: z.size} +} + +// Pen returns the location of the path-drawing pen: the last argument to the +// most recent XxxTo call. +func (z *Rasterizer) Pen() f32.Vec2 { + return z.pen +} + +// ClosePath closes the current path. +func (z *Rasterizer) ClosePath() { + z.LineTo(z.first) +} + +// MoveTo starts a new path and moves the pen to a. +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) MoveTo(a f32.Vec2) { + z.first = a + z.pen = a +} + +// LineTo adds a line segment, from the pen to b, and moves the pen to b. +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) LineTo(b f32.Vec2) { + // TODO: add a fixed point math implementation. + z.floatingLineTo(b) +} + +// QuadTo adds a quadratic Bézier segment, from the pen via b to c, and moves +// the pen to c. +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) QuadTo(b, c f32.Vec2) { + a := z.pen + devsq := devSquared(a, b, c) + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + ab := lerp(t, a, b) + bc := lerp(t, b, c) + z.LineTo(lerp(t, ab, bc)) + } + } + z.LineTo(c) +} + +// CubeTo adds a cubic Bézier segment, from the pen via b and c to d, and moves +// the pen to d. +// +// The coordinates are allowed to be out of the Rasterizer's bounds. +func (z *Rasterizer) CubeTo(b, c, d f32.Vec2) { + a := z.pen + devsq := devSquared(a, b, d) + if devsqAlt := devSquared(a, c, d); devsq < devsqAlt { + devsq = devsqAlt + } + if devsq >= 0.333 { + const tol = 3 + n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq)))) + t, nInv := float32(0), 1/float32(n) + for i := 0; i < n-1; i++ { + t += nInv + ab := lerp(t, a, b) + bc := lerp(t, b, c) + cd := lerp(t, c, d) + abc := lerp(t, ab, bc) + bcd := lerp(t, bc, cd) + z.LineTo(lerp(t, abc, bcd)) + } + } + z.LineTo(d) +} + +// devSquared returns a measure of how curvy the sequnce a to b to c is. It +// determines how many line segments will approximate a Bézier curve segment. +// +// http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html +// gives the rationale for this evenly spaced heuristic instead of a recursive +// de Casteljau approach: +// +// The reason for the subdivision by n is that I expect the "flatness" +// computation to be semi-expensive (it's done once rather than on each +// potential subdivision) and also because you'll often get fewer subdivisions. +// Taking a circular arc as a simplifying assumption (ie a spherical cow), +// where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't +// made any horrible mistakes, is expected to be 33% more in the limit. +func devSquared(a, b, c f32.Vec2) float32 { + devx := a[0] - 2*b[0] + c[0] + devy := a[1] - 2*b[1] + c[1] + return devx*devx + devy*devy +} + +// Draw implements the Drawer interface from the standard library's image/draw +// package. +// +// The vector paths previously added via the XxxTo calls become the mask for +// drawing src onto dst. +func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { + if src, ok := src.(*image.Uniform); ok { + _, _, _, srcA := src.RGBA() + switch dst := dst.(type) { + case *image.Alpha: + // Fast path for glyph rendering. + if srcA == 0xffff && z.DrawOp == draw.Src { + z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r) + return + } + } + } + println("TODO: the general case") +} + +func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) { + // TODO: add SIMD implementations. + // TODO: add a fixed point math implementation. + // TODO: non-zero vs even-odd winding? + if r == dst.Bounds() && r == z.Bounds() { + floatingAccumulate(dst.Pix, z.area) + return + } + println("TODO: the general case") +} |