Example #1
0
File: geom.go Project: eswdd/bosun
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
	// 181/256 is approximately 1/√2, or sin(π/4).
	px, py := int64(p.X), int64(p.Y)
	qx := (+px + py) * 181 / 256
	qy := (-px + py) * 181 / 256
	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
Example #2
0
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index. yMax is the top of the glyph's bounding box.
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
	j := int(i)
	if j < 0 || f.nGlyph <= j {
		return VMetric{}
	}
	if 4*j+4 <= len(f.vmtx) {
		return VMetric{
			AdvanceHeight:  fixed.Int26_6(u16(f.vmtx, 4*j)),
			TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
		}
	}
	// The OS/2 table has grown over time.
	// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
	// says that it was originally 68 bytes. Optional fields, including
	// the ascender and descender, are described at
	// http://www.microsoft.com/typography/otspec/os2.htm
	if len(f.os2) >= 72 {
		sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
		sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
		return VMetric{
			AdvanceHeight:  sTypoAscender - sTypoDescender,
			TopSideBearing: sTypoAscender - yMax,
		}
	}
	return VMetric{
		AdvanceHeight:  fixed.Int26_6(f.fUnitsPerEm),
		TopSideBearing: 0,
	}
}
Example #3
0
// rasterize returns the advance width, glyph mask and integer-pixel offset
// to render the given glyph at the given sub-pixel offsets.
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
	fixed.Int26_6, *image.Alpha, image.Point, error) {

	if err := c.glyphBuf.Load(c.f, c.scale, glyph, c.hinting); err != nil {
		return 0, nil, image.Point{}, err
	}
	// Calculate the integer-pixel bounds for the glyph.
	xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6
	ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6
	xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6
	ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6
	if xmin > xmax || ymin > ymax {
		return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
	}
	// A TrueType's glyph's nodes can have negative co-ordinates, but the
	// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
	// the pixel offsets, based on the font's FUnit metrics, that let a
	// negative co-ordinate in TrueType space be non-negative in rasterizer
	// space. xmin and ymin are typically <= 0.
	fx -= fixed.Int26_6(xmin << 6)
	fy -= fixed.Int26_6(ymin << 6)
	// Rasterize the glyph's vectors.
	c.r.Clear()
	e0 := 0
	for _, e1 := range c.glyphBuf.Ends {
		c.drawContour(c.glyphBuf.Points[e0:e1], fx, fy)
		e0 = e1
	}
	a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
	c.r.Rasterize(raster.NewAlphaSrcPainter(a))
	return c.glyphBuf.AdvanceWidth, a, image.Point{xmin, ymin}, nil
}
Example #4
0
File: ftgc.go Project: eswdd/bosun
// 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 *GraphicContext) 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.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.glyphBuf.Ends {
			ps := gc.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
}
Example #5
0
// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
// The numerical values can be manually verified by examining luxisr.ttx.
func TestParse(t *testing.T) {
	f, _, err := parseTestdataFont("luxisr")
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.FUnitsPerEm(), int32(2048); got != want {
		t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
	}
	fupe := fixed.Int26_6(f.FUnitsPerEm())
	if got, want := f.Bounds(fupe), mkBounds(-441, -432, 2024, 2033); got != want {
		t.Errorf("Bounds: got %v, want %v", got, want)
	}

	i0 := f.Index('A')
	i1 := f.Index('V')
	if i0 != 36 || i1 != 57 {
		t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
	}
	if got, want := f.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
		t.Errorf("HMetric: got %v, want %v", got, want)
	}
	if got, want := f.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
		t.Errorf("VMetric: got %v, want %v", got, want)
	}
	if got, want := f.Kern(fupe, i0, i1), fixed.Int26_6(-144); got != want {
		t.Errorf("Kern: got %v, want %v", got, want)
	}

	g := &GlyphBuf{}
	err = g.Load(f, fupe, i0, font.HintingNone)
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	g0 := &GlyphBuf{
		Bounds: g.Bounds,
		Points: g.Points,
		Ends:   g.Ends,
	}
	g1 := &GlyphBuf{
		Bounds: mkBounds(19, 0, 1342, 1480),
		Points: []Point{
			{19, 0, 51},
			{581, 1480, 1},
			{789, 1480, 51},
			{1342, 0, 1},
			{1116, 0, 35},
			{962, 410, 3},
			{368, 410, 33},
			{214, 0, 3},
			{428, 566, 19},
			{904, 566, 33},
			{667, 1200, 3},
		},
		Ends: []int{8, 11},
	}
	if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
		t.Errorf("GlyphBuf:\ngot  %v\nwant %v", got, want)
	}
}
Example #6
0
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
	if x >= 0 {
		x += fixed.Int26_6(f.fUnitsPerEm) / 2
	} else {
		x -= fixed.Int26_6(f.fUnitsPerEm) / 2
	}
	return x / fixed.Int26_6(f.fUnitsPerEm)
}
Example #7
0
File: geom.go Project: eswdd/bosun
// pNorm returns the vector p normalized to the given length, or zero if p is
// degenerate.
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
	d := pLen(p)
	if d == 0 {
		return fixed.Point26_6{}
	}
	s, t := int64(length), int64(d)
	x := int64(p.X) * s / t
	y := int64(p.Y) * s / t
	return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
}
Example #8
0
File: face.go Project: eswdd/bosun
// NewFace returns a new font.Face for the given Font.
func NewFace(f *Font, opts *Options) font.Face {
	a := &face{
		f:          f,
		hinting:    opts.hinting(),
		scale:      fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
		glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
	}
	a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
	a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()

	// Fill the cache with invalid entries. Valid glyph cache entries have fx
	// and fy in the range [0, 64). Valid index cache entries have rune >= 0.
	for i := range a.glyphCache {
		a.glyphCache[i].key.fy = 0xff
	}
	for i := range a.indexCache {
		a.indexCache[i].rune = -1
	}

	// Set the rasterizer's bounds to be big enough to handle the largest glyph.
	b := f.Bounds(a.scale)
	xmin := +int(b.Min.X) >> 6
	ymin := -int(b.Max.Y) >> 6
	xmax := +int(b.Max.X+63) >> 6
	ymax := -int(b.Min.Y-63) >> 6
	a.maxw = xmax - xmin
	a.maxh = ymax - ymin
	a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
	a.r.SetBounds(a.maxw, a.maxh)
	a.p = facePainter{a}

	return a
}
Example #9
0
File: hint.go Project: eswdd/bosun
// dotProduct returns the dot product of [x, y] and q. It is almost the same as
//	px := int64(x)
//	py := int64(y)
//	qx := int64(q[0])
//	qy := int64(q[1])
//	return fixed.Int26_6((px*qx + py*qy + 1<<13) >> 14)
// except that the computation is done with 32-bit integers to produce exactly
// the same rounding behavior as C Freetype.
func dotProduct(x, y fixed.Int26_6, q [2]f2dot14) fixed.Int26_6 {
	// Compute x*q[0] as 64-bit value.
	l := uint32((int32(x) & 0xFFFF) * int32(q[0]))
	m := (int32(x) >> 16) * int32(q[0])

	lo1 := l + (uint32(m) << 16)
	hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l)

	// Compute y*q[1] as 64-bit value.
	l = uint32((int32(y) & 0xFFFF) * int32(q[1]))
	m = (int32(y) >> 16) * int32(q[1])

	lo2 := l + (uint32(m) << 16)
	hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l)

	// Add them.
	lo := lo1 + lo2
	hi := hi1 + hi2 + bool2int32(lo < lo1)

	// Divide the result by 2^14 with rounding.
	s := hi >> 31
	l = lo + uint32(s)
	hi += s + bool2int32(l < lo)
	lo = l

	l = lo + 0x2000
	hi += bool2int32(l < lo)

	return fixed.Int26_6((uint32(hi) << 18) | (l >> 14))
}
Example #10
0
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
	j := int(i)
	if j < 0 || f.nGlyph <= j {
		return HMetric{}
	}
	if j >= f.nHMetric {
		p := 4 * (f.nHMetric - 1)
		return HMetric{
			AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, p)),
			LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
		}
	}
	return HMetric{
		AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, 4*j)),
		LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
	}
}
Example #11
0
// Add2 adds a quadratic segment to the current curve.
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
	// Calculate nSplit (the number of recursive decompositions) based on how
	// 'curvy' it is. Specifically, how much the middle point b deviates from
	// (a+c)/2.
	dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
	nsplit := 0
	for dev > 0 {
		dev /= 4
		nsplit++
	}
	// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
	// is 16.
	const maxNsplit = 16
	if nsplit > maxNsplit {
		panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
	}
	// Recursively decompose the curve nSplit levels deep.
	var (
		pStack [2*maxNsplit + 3]fixed.Point26_6
		sStack [maxNsplit + 1]int
		i      int
	)
	sStack[0] = nsplit
	pStack[0] = c
	pStack[1] = b
	pStack[2] = r.a
	for i >= 0 {
		s := sStack[i]
		p := pStack[2*i:]
		if s > 0 {
			// Split the quadratic curve p[:3] into an equivalent set of two
			// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
			// and p[0] is unchanged.
			mx := p[1].X
			p[4].X = p[2].X
			p[3].X = (p[4].X + mx) / 2
			p[1].X = (p[0].X + mx) / 2
			p[2].X = (p[1].X + p[3].X) / 2
			my := p[1].Y
			p[4].Y = p[2].Y
			p[3].Y = (p[4].Y + my) / 2
			p[1].Y = (p[0].Y + my) / 2
			p[2].Y = (p[1].Y + p[3].Y) / 2
			// The two shorter curves have one less split to do.
			sStack[i] = s - 1
			sStack[i+1] = s - 1
			i++
		} else {
			// Replace the level-0 quadratic with a two-linear-piece
			// approximation.
			midx := (p[0].X + 2*p[1].X + p[2].X) / 4
			midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
			r.Add1(fixed.Point26_6{midx, midy})
			r.Add1(p[0])
			i--
		}
	}
}
Example #12
0
File: text.go Project: eswdd/bosun
// Extents returns the FontExtents for a font.
// TODO needs to read this https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#intro
func Extents(font *truetype.Font, size float64) FontExtents {
	bounds := font.Bounds(fixed.Int26_6(font.FUnitsPerEm()))
	scale := size / float64(font.FUnitsPerEm())
	return FontExtents{
		Ascent:  float64(bounds.Max.Y) * scale,
		Descent: float64(bounds.Min.Y) * scale,
		Height:  float64(bounds.Max.Y-bounds.Min.Y) * scale,
	}
}
Example #13
0
func (f *Font) parseHead() error {
	if len(f.head) != 54 {
		return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
	}
	f.fUnitsPerEm = int32(u16(f.head, 18))
	f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
	f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
	f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
	f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
	switch i := u16(f.head, 50); i {
	case 0:
		f.locaOffsetFormat = locaOffsetFormatShort
	case 1:
		f.locaOffsetFormat = locaOffsetFormatLong
	default:
		return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
	}
	return nil
}
Example #14
0
File: ftgc.go Project: eswdd/bosun
func (gc *GraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
	if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), glyph, font.HintingNone); err != nil {
		return err
	}
	e0 := 0
	for _, e1 := range gc.glyphBuf.Ends {
		DrawContour(gc, gc.glyphBuf.Points[e0:e1], dx, dy)
		e0 = e1
	}
	return nil
}
Example #15
0
File: ftgc.go Project: eswdd/bosun
// 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 *GraphicContext) 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
}
Example #16
0
File: hint.go Project: eswdd/bosun
func (h *hinter) move(p *Point, distance fixed.Int26_6, touch bool) {
	fvx := int64(h.gs.fv[0])
	pvx := int64(h.gs.pv[0])
	if fvx == 0x4000 && pvx == 0x4000 {
		p.X += fixed.Int26_6(distance)
		if touch {
			p.Flags |= flagTouchedX
		}
		return
	}

	fvy := int64(h.gs.fv[1])
	pvy := int64(h.gs.pv[1])
	if fvy == 0x4000 && pvy == 0x4000 {
		p.Y += fixed.Int26_6(distance)
		if touch {
			p.Flags |= flagTouchedY
		}
		return
	}

	fvDotPv := (fvx*pvx + fvy*pvy) >> 14

	if fvx != 0 {
		p.X += fixed.Int26_6(mulDiv(fvx, int64(distance), fvDotPv))
		if touch {
			p.Flags |= flagTouchedX
		}
	}

	if fvy != 0 {
		p.Y += fixed.Int26_6(mulDiv(fvy, int64(distance), fvDotPv))
		if touch {
			p.Flags |= flagTouchedY
		}
	}
}
Example #17
0
File: hint.go Project: eswdd/bosun
func (h *hinter) initializeScaledCVT() {
	h.scaledCVTInitialized = true
	if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) {
		h.scaledCVT = h.scaledCVT[:n]
	} else {
		if n < 32 {
			n = 32
		}
		h.scaledCVT = make([]fixed.Int26_6, len(h.font.cvt)/2, n)
	}
	for i := range h.scaledCVT {
		unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1])
		h.scaledCVT[i] = h.font.scale(h.scale * fixed.Int26_6(int16(unscaled)))
	}
}
Example #18
0
File: face.go Project: eswdd/bosun
// rasterize returns the advance width, integer-pixel offset to render at, and
// the width and height of the given glyph at the given sub-pixel offsets.
//
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
	if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
		return glyphCacheVal{}, false
	}
	// Calculate the integer-pixel bounds for the glyph.
	xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
	ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
	xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
	ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
	if xmin > xmax || ymin > ymax {
		return glyphCacheVal{}, false
	}
	// A TrueType's glyph's nodes can have negative co-ordinates, but the
	// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
	// the pixel offsets, based on the font's FUnit metrics, that let a
	// negative co-ordinate in TrueType space be non-negative in rasterizer
	// space. xmin and ymin are typically <= 0.
	fx -= fixed.Int26_6(xmin << 6)
	fy -= fixed.Int26_6(ymin << 6)
	// Rasterize the glyph's vectors.
	a.r.Clear()
	pixOffset := a.paintOffset * a.maxw
	clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
	e0 := 0
	for _, e1 := range a.glyphBuf.Ends {
		a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
		e0 = e1
	}
	a.r.Rasterize(a.p)
	return glyphCacheVal{
		a.glyphBuf.AdvanceWidth,
		image.Point{xmin, ymin},
		xmax - xmin,
		ymax - ymin,
	}, true
}
Example #19
0
// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (c *Context) recalc() {
	c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0))
	if c.f == nil {
		c.r.SetBounds(0, 0)
	} else {
		// Set the rasterizer's bounds to be big enough to handle the largest glyph.
		b := c.f.Bounds(c.scale)
		xmin := +int(b.Min.X) >> 6
		ymin := -int(b.Max.Y) >> 6
		xmax := +int(b.Max.X+63) >> 6
		ymax := -int(b.Min.Y-63) >> 6
		c.r.SetBounds(xmax-xmin, ymax-ymin)
	}
	for i := range c.cache {
		c.cache[i] = cacheEntry{}
	}
}
Example #20
0
// Kern returns the horizontal adjustment for the given glyph pair. A positive
// kern means to move the glyphs further apart.
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
	if f.nKern == 0 {
		return 0
	}
	g := uint32(i0)<<16 | uint32(i1)
	lo, hi := 0, f.nKern
	for lo < hi {
		i := (lo + hi) / 2
		ig := u32(f.kern, 18+6*i)
		if ig < g {
			lo = i + 1
		} else if ig > g {
			hi = i
		} else {
			return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
		}
	}
	return 0
}
Example #21
0
// scalingTestParse parses a line of points like
// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1
// The line will not have a trailing "\n".
func scalingTestParse(line string) (ret scalingTestData) {
	next := func(s string) (string, fixed.Int26_6) {
		t, i := "", strings.Index(s, " ")
		if i != -1 {
			s, t = s[:i], s[i+1:]
		}
		x, _ := strconv.Atoi(s)
		return t, fixed.Int26_6(x)
	}

	i := strings.Index(line, ";")
	prefix, line := line[:i], line[i+1:]

	prefix, ret.advanceWidth = next(prefix)
	prefix, ret.bounds.Min.X = next(prefix)
	prefix, ret.bounds.Min.Y = next(prefix)
	prefix, ret.bounds.Max.X = next(prefix)
	prefix, ret.bounds.Max.Y = next(prefix)

	ret.points = make([]Point, 0, 1+strings.Count(line, ","))
	for len(line) > 0 {
		s := line
		if i := strings.Index(line, ","); i != -1 {
			s, line = line[:i], line[i+1:]
			for len(line) > 0 && line[0] == ' ' {
				line = line[1:]
			}
		} else {
			line = ""
		}
		s, x := next(s)
		s, y := next(s)
		s, f := next(s)
		ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)})
	}
	return ret
}
Example #22
0
// Add1 adds a linear segment to the current curve.
func (r *Rasterizer) Add1(b fixed.Point26_6) {
	x0, y0 := r.a.X, r.a.Y
	x1, y1 := b.X, b.Y
	dx, dy := x1-x0, y1-y0
	// Break the 26.6 fixed point Y co-ordinates into integral and fractional
	// parts.
	y0i := int(y0) / 64
	y0f := y0 - fixed.Int26_6(64*y0i)
	y1i := int(y1) / 64
	y1f := y1 - fixed.Int26_6(64*y1i)

	if y0i == y1i {
		// There is only one scanline.
		r.scan(y0i, x0, y0f, x1, y1f)

	} else if dx == 0 {
		// This is a vertical line segment. We avoid calling r.scan and instead
		// manipulate r.area and r.cover directly.
		var (
			edge0, edge1 fixed.Int26_6
			yiDelta      int
		)
		if dy > 0 {
			edge0, edge1, yiDelta = 0, 64, 1
		} else {
			edge0, edge1, yiDelta = 64, 0, -1
		}
		x0i, yi := int(x0)/64, y0i
		x0fTimes2 := (int(x0) - (64 * x0i)) * 2
		// Do the first pixel.
		dcover := int(edge1 - y0f)
		darea := int(x0fTimes2 * dcover)
		r.area += darea
		r.cover += dcover
		yi += yiDelta
		r.setCell(x0i, yi)
		// Do all the intermediate pixels.
		dcover = int(edge1 - edge0)
		darea = int(x0fTimes2 * dcover)
		for yi != y1i {
			r.area += darea
			r.cover += dcover
			yi += yiDelta
			r.setCell(x0i, yi)
		}
		// Do the last pixel.
		dcover = int(y1f - edge0)
		darea = int(x0fTimes2 * dcover)
		r.area += darea
		r.cover += dcover

	} else {
		// There are at least two scanlines. Apart from the first and last
		// scanlines, all intermediate scanlines go through the full height of
		// the row, or 64 units in 26.6 fixed point format.
		var (
			p, q, edge0, edge1 fixed.Int26_6
			yiDelta            int
		)
		if dy > 0 {
			p, q = (64-y0f)*dx, dy
			edge0, edge1, yiDelta = 0, 64, 1
		} else {
			p, q = y0f*dx, -dy
			edge0, edge1, yiDelta = 64, 0, -1
		}
		xDelta, xRem := p/q, p%q
		if xRem < 0 {
			xDelta -= 1
			xRem += q
		}
		// Do the first scanline.
		x, yi := x0, y0i
		r.scan(yi, x, y0f, x+xDelta, edge1)
		x, yi = x+xDelta, yi+yiDelta
		r.setCell(int(x)/64, yi)
		if yi != y1i {
			// Do all the intermediate scanlines.
			p = 64 * dx
			fullDelta, fullRem := p/q, p%q
			if fullRem < 0 {
				fullDelta -= 1
				fullRem += q
			}
			xRem -= q
			for yi != y1i {
				xDelta = fullDelta
				xRem += fullRem
				if xRem >= 0 {
					xDelta += 1
					xRem -= q
				}
				r.scan(yi, x, edge0, x+xDelta, edge1)
				x, yi = x+xDelta, yi+yiDelta
				r.setCell(int(x)/64, yi)
			}
		}
		// Do the last scanline.
		r.scan(yi, x, edge0, x1, y1f)
	}
	// The next lineTo starts from b.
	r.a = b
}
Example #23
0
// scan accumulates area/coverage for the yi'th scanline, going from
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
// and from y0f to y1f fractional vertical units within that scanline.
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
	// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
	x0i := int(x0) / 64
	x0f := x0 - fixed.Int26_6(64*x0i)
	x1i := int(x1) / 64
	x1f := x1 - fixed.Int26_6(64*x1i)

	// A perfectly horizontal scan.
	if y0f == y1f {
		r.setCell(x1i, yi)
		return
	}
	dx, dy := x1-x0, y1f-y0f
	// A single cell scan.
	if x0i == x1i {
		r.area += int((x0f + x1f) * dy)
		r.cover += int(dy)
		return
	}
	// There are at least two cells. Apart from the first and last cells,
	// all intermediate cells go through the full width of the cell,
	// or 64 units in 26.6 fixed point format.
	var (
		p, q, edge0, edge1 fixed.Int26_6
		xiDelta            int
	)
	if dx > 0 {
		p, q = (64-x0f)*dy, dx
		edge0, edge1, xiDelta = 0, 64, 1
	} else {
		p, q = x0f*dy, -dx
		edge0, edge1, xiDelta = 64, 0, -1
	}
	yDelta, yRem := p/q, p%q
	if yRem < 0 {
		yDelta -= 1
		yRem += q
	}
	// Do the first cell.
	xi, y := x0i, y0f
	r.area += int((x0f + edge1) * yDelta)
	r.cover += int(yDelta)
	xi, y = xi+xiDelta, y+yDelta
	r.setCell(xi, yi)
	if xi != x1i {
		// Do all the intermediate cells.
		p = 64 * (y1f - y + yDelta)
		fullDelta, fullRem := p/q, p%q
		if fullRem < 0 {
			fullDelta -= 1
			fullRem += q
		}
		yRem -= q
		for xi != x1i {
			yDelta = fullDelta
			yRem += fullRem
			if yRem >= 0 {
				yDelta += 1
				yRem -= q
			}
			r.area += int(64 * yDelta)
			r.cover += int(yDelta)
			xi, y = xi+xiDelta, y+yDelta
			r.setCell(xi, yi)
		}
	}
	// Do the last cell.
	yDelta = y1f - y
	r.area += int((edge0 + x1f) * yDelta)
	r.cover += int(yDelta)
}
Example #24
0
File: glyph.go Project: eswdd/bosun
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
	// The recursion limit here is arbitrary, but defends against malformed glyphs.
	if recursion >= 32 {
		return UnsupportedError("excessive compound glyph recursion")
	}
	// Find the relevant slice of g.font.glyf.
	var g0, g1 uint32
	if g.font.locaOffsetFormat == locaOffsetFormatShort {
		g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
		g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
	} else {
		g0 = u32(g.font.loca, 4*int(i))
		g1 = u32(g.font.loca, 4*int(i)+4)
	}

	// Decode the contour count and nominal bounding box, from the first
	// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
	// and 6, are unused.
	glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
	if g0+10 <= g1 {
		glyf = g.font.glyf[g0:g1]
		ne = int(int16(u16(glyf, 0)))
		boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
		boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
	}

	// Create the phantom points.
	uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
	uvm := g.font.unscaledVMetric(i, boundsYMax)
	g.phantomPoints = [4]Point{
		{X: boundsXMin - uhm.LeftSideBearing},
		{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
	}
	if len(glyf) == 0 {
		g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
		copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
		g.Points = g.Points[:len(g.Points)-4]
		return nil
	}

	// Load and hint the contours.
	if ne < 0 {
		if ne != -1 {
			// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
			// "the values -2, -3, and so forth, are reserved for future use."
			return UnsupportedError("negative number of contours")
		}
		pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
		if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
			return err
		}
	} else {
		np0, ne0 := len(g.Points), len(g.Ends)
		program := g.loadSimple(glyf, ne)
		g.addPhantomsAndScale(np0, np0, true, true)
		pp1x = g.Points[len(g.Points)-4].X
		if g.hinting != font.HintingNone {
			if len(program) != 0 {
				err := g.hinter.run(
					program,
					g.Points[np0:],
					g.Unhinted[np0:],
					g.InFontUnits[np0:],
					g.Ends[ne0:],
				)
				if err != nil {
					return err
				}
			}
			// Drop the four phantom points.
			g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
			g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
		}
		if useMyMetrics {
			copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
		}
		g.Points = g.Points[:len(g.Points)-4]
		if np0 != 0 {
			// The hinting program expects the []Ends values to be indexed
			// relative to the inner glyph, not the outer glyph, so we delay
			// adding np0 until after the hinting program (if any) has run.
			for i := ne0; i < len(g.Ends); i++ {
				g.Ends[i] += np0
			}
		}
	}
	if useMyMetrics && !g.metricsSet {
		g.metricsSet = true
		g.pp1x = pp1x
	}
	return nil
}
Example #25
0
File: face.go Project: eswdd/bosun
// subPixels returns q and the bias and mask that leads to q quantized
// sub-pixel locations per full pixel.
//
// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
// because we want to round fractions of fixed.Int26_6 as:
//	-  0 to  7 rounds to 0.
//	-  8 to 23 rounds to 16.
//	- 24 to 39 rounds to 32.
//	- 40 to 55 rounds to 48.
//	- 56 to 63 rounds to 64.
// which means to add 8 and then bitwise-and with -16, in two's complement
// representation.
//
// When q ==  1, we want bias == 32 and mask == -64.
// When q ==  2, we want bias == 16 and mask == -32.
// When q ==  4, we want bias ==  8 and mask == -16.
// ...
// When q == 64, we want bias ==  0 and mask ==  -1. (The no-op case).
// The pattern is clear.
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
	return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
}
Example #26
0
File: glyph.go Project: eswdd/bosun
// Load loads a glyph's contours from a Font, overwriting any previously loaded
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
// 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
	g.Points = g.Points[:0]
	g.Unhinted = g.Unhinted[:0]
	g.InFontUnits = g.InFontUnits[:0]
	g.Ends = g.Ends[:0]
	g.font = f
	g.hinting = h
	g.scale = scale
	g.pp1x = 0
	g.phantomPoints = [4]Point{}
	g.metricsSet = false

	if h != font.HintingNone {
		if err := g.hinter.init(f, scale); err != nil {
			return err
		}
	}
	if err := g.load(0, i, true); err != nil {
		return err
	}
	// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
	// and should be cleaned up once we have all the testScaling tests passing,
	// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
	pp1x := g.pp1x
	if h != font.HintingNone {
		pp1x = g.phantomPoints[0].X
	}
	if pp1x != 0 {
		for i := range g.Points {
			g.Points[i].X -= pp1x
		}
	}

	advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
	if h != font.HintingNone {
		if len(f.hdmx) >= 8 {
			if n := u32(f.hdmx, 4); n > 3+uint32(i) {
				for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
					if fixed.Int26_6(hdmx[0]) == scale>>6 {
						advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
						break
					}
				}
			}
		}
		advanceWidth = (advanceWidth + 32) &^ 63
	}
	g.AdvanceWidth = advanceWidth

	// Set g.Bounds to the 'control box', which is the bounding box of the
	// Bézier curves' control points. This is easier to calculate, no smaller
	// than and often equal to the tightest possible bounding box of the curves
	// themselves. This approach is what C Freetype does. We can't just scale
	// the nominal bounding box in the glyf data as the hinting process and
	// phantom point adjustment may move points outside of that box.
	if len(g.Points) == 0 {
		g.Bounds = fixed.Rectangle26_6{}
	} else {
		p := g.Points[0]
		g.Bounds.Min.X = p.X
		g.Bounds.Max.X = p.X
		g.Bounds.Min.Y = p.Y
		g.Bounds.Max.Y = p.Y
		for _, p := range g.Points[1:] {
			if g.Bounds.Min.X > p.X {
				g.Bounds.Min.X = p.X
			} else if g.Bounds.Max.X < p.X {
				g.Bounds.Max.X = p.X
			}
			if g.Bounds.Min.Y > p.Y {
				g.Bounds.Min.Y = p.Y
			} else if g.Bounds.Max.Y < p.Y {
				g.Bounds.Max.Y = p.Y
			}
		}
		// Snap the box to the grid, if hinting is on.
		if h != font.HintingNone {
			g.Bounds.Min.X &^= 63
			g.Bounds.Min.Y &^= 63
			g.Bounds.Max.X += 63
			g.Bounds.Max.X &^= 63
			g.Bounds.Max.Y += 63
			g.Bounds.Max.Y &^= 63
		}
	}
	return nil
}
Example #27
0
File: glyph.go Project: eswdd/bosun
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
	glyf []byte, useMyMetrics bool) error {

	// Flags for decoding a compound glyph. These flags are documented at
	// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
	const (
		flagArg1And2AreWords = 1 << iota
		flagArgsAreXYValues
		flagRoundXYToGrid
		flagWeHaveAScale
		flagUnused
		flagMoreComponents
		flagWeHaveAnXAndYScale
		flagWeHaveATwoByTwo
		flagWeHaveInstructions
		flagUseMyMetrics
		flagOverlapCompound
	)
	np0, ne0 := len(g.Points), len(g.Ends)
	offset := loadOffset
	for {
		flags := u16(glyf, offset)
		component := Index(u16(glyf, offset+2))
		dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
		if flags&flagArg1And2AreWords != 0 {
			dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
			dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
			offset += 8
		} else {
			dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
			dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
			offset += 6
		}
		if flags&flagArgsAreXYValues == 0 {
			return UnsupportedError("compound glyph transform vector")
		}
		if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
			hasTransform = true
			switch {
			case flags&flagWeHaveAScale != 0:
				transform[0] = int16(u16(glyf, offset+0))
				transform[3] = transform[0]
				offset += 2
			case flags&flagWeHaveAnXAndYScale != 0:
				transform[0] = int16(u16(glyf, offset+0))
				transform[3] = int16(u16(glyf, offset+2))
				offset += 4
			case flags&flagWeHaveATwoByTwo != 0:
				transform[0] = int16(u16(glyf, offset+0))
				transform[1] = int16(u16(glyf, offset+2))
				transform[2] = int16(u16(glyf, offset+4))
				transform[3] = int16(u16(glyf, offset+6))
				offset += 8
			}
		}
		savedPP := g.phantomPoints
		np0 := len(g.Points)
		componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
		if err := g.load(recursion+1, component, componentUMM); err != nil {
			return err
		}
		if flags&flagUseMyMetrics == 0 {
			g.phantomPoints = savedPP
		}
		if hasTransform {
			for j := np0; j < len(g.Points); j++ {
				p := &g.Points[j]
				newX := 0 +
					fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
					fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
				newY := 0 +
					fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
					fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
				p.X, p.Y = newX, newY
			}
		}
		dx = g.font.scale(g.scale * dx)
		dy = g.font.scale(g.scale * dy)
		if flags&flagRoundXYToGrid != 0 {
			dx = (dx + 32) &^ 63
			dy = (dy + 32) &^ 63
		}
		for j := np0; j < len(g.Points); j++ {
			p := &g.Points[j]
			p.X += dx
			p.Y += dy
		}
		// TODO: also adjust g.InFontUnits and g.Unhinted?
		if flags&flagMoreComponents == 0 {
			break
		}
	}

	instrLen := 0
	if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
		instrLen = int(u16(glyf, offset))
		offset += 2
	}

	g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
	points, ends := g.Points[np0:], g.Ends[ne0:]
	g.Points = g.Points[:len(g.Points)-4]
	for j := range points {
		points[j].Flags &^= flagTouchedX | flagTouchedY
	}

	if instrLen == 0 {
		if !g.metricsSet {
			copy(g.phantomPoints[:], points[len(points)-4:])
		}
		return nil
	}

	// Hint the compound glyph.
	program := glyf[offset : offset+instrLen]
	// Temporarily adjust the ends to be relative to this compound glyph.
	if np0 != 0 {
		for i := range ends {
			ends[i] -= np0
		}
	}
	// Hinting instructions of a composite glyph completely refer to the
	// (already) hinted subglyphs.
	g.tmp = append(g.tmp[:0], points...)
	if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
		return err
	}
	if np0 != 0 {
		for i := range ends {
			ends[i] += np0
		}
	}
	if !g.metricsSet {
		copy(g.phantomPoints[:], points[len(points)-4:])
	}
	return nil
}
Example #28
0
File: glyph.go Project: eswdd/bosun
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
	offset := loadOffset
	for i := 0; i < ne; i++ {
		g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
		offset += 2
	}

	// Note the TrueType hinting instructions.
	instrLen := int(u16(glyf, offset))
	offset += 2
	program = glyf[offset : offset+instrLen]
	offset += instrLen

	np0 := len(g.Points)
	np1 := np0 + int(g.Ends[len(g.Ends)-1])

	// Decode the flags.
	for i := np0; i < np1; {
		c := uint32(glyf[offset])
		offset++
		g.Points = append(g.Points, Point{Flags: c})
		i++
		if c&flagRepeat != 0 {
			count := glyf[offset]
			offset++
			for ; count > 0; count-- {
				g.Points = append(g.Points, Point{Flags: c})
				i++
			}
		}
	}

	// Decode the co-ordinates.
	var x int16
	for i := np0; i < np1; i++ {
		f := g.Points[i].Flags
		if f&flagXShortVector != 0 {
			dx := int16(glyf[offset])
			offset++
			if f&flagPositiveXShortVector == 0 {
				x -= dx
			} else {
				x += dx
			}
		} else if f&flagThisXIsSame == 0 {
			x += int16(u16(glyf, offset))
			offset += 2
		}
		g.Points[i].X = fixed.Int26_6(x)
	}
	var y int16
	for i := np0; i < np1; i++ {
		f := g.Points[i].Flags
		if f&flagYShortVector != 0 {
			dy := int16(glyf[offset])
			offset++
			if f&flagPositiveYShortVector == 0 {
				y -= dy
			} else {
				y += dy
			}
		} else if f&flagThisYIsSame == 0 {
			y += int16(u16(glyf, offset))
			offset += 2
		}
		g.Points[i].Y = fixed.Int26_6(y)
	}

	return program
}
Example #29
0
// Add3 adds a cubic segment to the current curve.
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
	// Calculate nSplit (the number of recursive decompositions) based on how
	// 'curvy' it is.
	dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
	dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
	nsplit := 0
	for dev2 > 0 || dev3 > 0 {
		dev2 /= 8
		dev3 /= 4
		nsplit++
	}
	// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
	// maxNsplit is 16.
	const maxNsplit = 16
	if nsplit > maxNsplit {
		panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
	}
	// Recursively decompose the curve nSplit levels deep.
	var (
		pStack [3*maxNsplit + 4]fixed.Point26_6
		sStack [maxNsplit + 1]int
		i      int
	)
	sStack[0] = nsplit
	pStack[0] = d
	pStack[1] = c
	pStack[2] = b
	pStack[3] = r.a
	for i >= 0 {
		s := sStack[i]
		p := pStack[3*i:]
		if s > 0 {
			// Split the cubic curve p[:4] into an equivalent set of two
			// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
			// and p[0] is unchanged.
			m01x := (p[0].X + p[1].X) / 2
			m12x := (p[1].X + p[2].X) / 2
			m23x := (p[2].X + p[3].X) / 2
			p[6].X = p[3].X
			p[5].X = m23x
			p[1].X = m01x
			p[2].X = (m01x + m12x) / 2
			p[4].X = (m12x + m23x) / 2
			p[3].X = (p[2].X + p[4].X) / 2
			m01y := (p[0].Y + p[1].Y) / 2
			m12y := (p[1].Y + p[2].Y) / 2
			m23y := (p[2].Y + p[3].Y) / 2
			p[6].Y = p[3].Y
			p[5].Y = m23y
			p[1].Y = m01y
			p[2].Y = (m01y + m12y) / 2
			p[4].Y = (m12y + m23y) / 2
			p[3].Y = (p[2].Y + p[4].Y) / 2
			// The two shorter curves have one less split to do.
			sStack[i] = s - 1
			sStack[i+1] = s - 1
			i++
		} else {
			// Replace the level-0 cubic with a two-linear-piece approximation.
			midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
			midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
			r.Add1(fixed.Point26_6{midx, midy})
			r.Add1(p[0])
			i--
		}
	}
}
Example #30
0
File: geom.go Project: eswdd/bosun
// pLen returns the length of the vector p.
func pLen(p fixed.Point26_6) fixed.Int26_6 {
	// TODO(nigeltao): use fixed point math.
	x := float64(p.X)
	y := float64(p.Y)
	return fixed.Int26_6(math.Sqrt(x*x + y*y))
}