func p(n node) fixed.Point26_6 { x, y := 20+n.x/4, 380-n.y/4 return fixed.Point26_6{ X: fixed.Int26_6(x << 6), Y: fixed.Int26_6(y << 6), } }
// 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, } }
// 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 }
// 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)} }
// 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) } }
// 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) }
// 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)} }
// 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)) }
// 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 }
func (f *subface) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { r -= f.firstRune if r < 0 || f.n <= int(r) { return 0, false } return fixed.Int26_6(f.fontchars[r].width) << 6, true }
func main() { flag.Parse() fmt.Printf("Loading fontfile %q\n", *fontfile) b, err := ioutil.ReadFile(*fontfile) if err != nil { log.Println(err) return } f, err := truetype.Parse(b) if err != nil { log.Println(err) return } fupe := fixed.Int26_6(f.FUnitsPerEm()) printBounds(f.Bounds(fupe)) fmt.Printf("FUnitsPerEm:%d\n\n", fupe) c0, c1 := 'A', 'V' i0 := f.Index(c0) hm := f.HMetric(fupe, i0) g := &truetype.GlyphBuf{} err = g.Load(f, fupe, i0, font.HintingNone) if err != nil { log.Println(err) return } fmt.Printf("'%c' glyph\n", c0) fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing) printGlyph(g) i1 := f.Index(c1) fmt.Printf("\n'%c', '%c' Kern:%d\n", c0, c1, f.Kern(fupe, i0, i1)) }
// 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))), } }
// 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-- } } }
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 }
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 } } }
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))) } }
// 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 }
func (f *subface) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { r -= f.firstRune if r < 0 || f.n <= int(r) { return fixed.Rectangle26_6{}, 0, false } i := &f.fontchars[r+0] j := &f.fontchars[r+1] bounds = fixed.R( int(i.left), int(i.top)-f.ascent, int(i.left)+int(j.x-i.x), int(i.bottom)-f.ascent, ) return bounds, fixed.Int26_6(i.width) << 6, true }
// 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{} } }
// 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 }
// 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 }
func (f *subface) Glyph(dot fixed.Point26_6, r rune) ( dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { r -= f.firstRune if r < 0 || f.n <= int(r) { return image.Rectangle{}, nil, image.Point{}, 0, false } i := &f.fontchars[r+0] j := &f.fontchars[r+1] minX := int(dot.X+32)>>6 + int(i.left) minY := int(dot.Y+32)>>6 + int(i.top) - f.ascent dr = image.Rectangle{ Min: image.Point{ X: minX, Y: minY, }, Max: image.Point{ X: minX + int(j.x-i.x), Y: minY + int(i.bottom) - int(i.top), }, } return dr, f.img, image.Point{int(i.x), int(i.top)}, fixed.Int26_6(i.width) << 6, true }
func p(x, y int) fixed.Point26_6 { return fixed.Point26_6{ X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64), } }
func main() { const ( n = 17 r = 64 * 80 ) s := fixed.Int26_6(r * math.Sqrt(2) / 2) t := fixed.Int26_6(r * math.Tan(math.Pi/8)) m := image.NewRGBA(image.Rect(0, 0, 800, 600)) draw.Draw(m, m.Bounds(), image.NewUniform(color.RGBA{63, 63, 63, 255}), image.ZP, draw.Src) mp := raster.NewRGBAPainter(m) mp.SetColor(image.Black) z := raster.NewRasterizer(800, 600) for i := 0; i < n; i++ { cx := fixed.Int26_6(6400 + 12800*(i%4)) cy := fixed.Int26_6(640 + 8000*(i/4)) c := fixed.Point26_6{X: cx, Y: cy} theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1)) dx := fixed.Int26_6(r * math.Cos(theta)) dy := fixed.Int26_6(r * math.Sin(theta)) d := fixed.Point26_6{X: dx, Y: dy} // Draw a quarter-circle approximated by two quadratic segments, // with each segment spanning 45 degrees. z.Start(c) z.Add1(c.Add(fixed.Point26_6{X: r, Y: 0})) z.Add2(c.Add(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s})) z.Add2(c.Add(fixed.Point26_6{X: t, Y: r}), c.Add(fixed.Point26_6{X: 0, Y: r})) // Add another quadratic segment whose angle ranges between 0 and 90 // degrees. For an explanation of the magic constants 128, 150, 181 and // 256, read the comments in the freetype/raster package. dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r) multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2 z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d)) // Close the curve. z.Add1(c) } z.Rasterize(mp) for i := 0; i < n; i++ { cx := fixed.Int26_6(6400 + 12800*(i%4)) cy := fixed.Int26_6(640 + 8000*(i/4)) for j := 0; j < n; j++ { theta := math.Pi * float64(j) / (n - 1) dx := fixed.Int26_6(r * math.Cos(theta)) dy := fixed.Int26_6(r * math.Sin(theta)) m.Set(int((cx+dx)/64), int((cy+dy)/64), color.RGBA{255, 255, 0, 255}) } } // Save that RGBA image to disk. outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer outFile.Close() b := bufio.NewWriter(outFile) err = png.Encode(b, m) if err != nil { log.Println(err) os.Exit(1) } err = b.Flush() if err != nil { log.Println(err) os.Exit(1) } fmt.Println("Wrote out.png OK.") }
// 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 }
// 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) }
// PointToFixed converts the given number of points (as in "a 12 point font") // into a 26.6 fixed point number of pixels. func (c *Context) PointToFixed(x float64) fixed.Int26_6 { return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0)) }
// Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6 // co-ordinate pair measured in fixed.Int26_6 units. func Pt(x, y int) fixed.Point26_6 { return fixed.Point26_6{ X: fixed.Int26_6(x << 6), Y: fixed.Int26_6(y << 6), } }
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of // the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The // two vectors n0 and n1 must be of equal length. func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) { // r2 is the square of the length of n0. r2 := pDot(n0, n0) if r2 < epsilon { // The arc radius is so small that we collapse to a straight line. p.Add1(pivot.Add(n1)) return } // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus // a final quadratic segment from s to n1. Each 45-degree segment has // control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, // rotated and translated. tan(π/8) is approximately 106/256. const tpo8 = 106 var s fixed.Point26_6 // We determine which octant the angle between n0 and n1 is in via three // dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 // degrees. m0 := pRot45CW(n0) m1 := pRot90CW(n0) m2 := pRot90CW(m0) if pDot(m1, n1) >= 0 { if pDot(n0, n1) >= 0 { if pDot(m2, n1) <= 0 { // n1 is between 0 and 45 degrees clockwise of n0. s = n0 } else { // n1 is between 45 and 90 degrees clockwise of n0. p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) s = m0 } } else { pm1, n0t := pivot.Add(m1), n0.Mul(tpo8) p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) p.Add2(pm1.Add(n0t), pm1) if pDot(m0, n1) >= 0 { // n1 is between 90 and 135 degrees clockwise of n0. s = m1 } else { // n1 is between 135 and 180 degrees clockwise of n0. p.Add2(pm1.Sub(n0t), pivot.Add(m2)) s = m2 } } } else { if pDot(n0, n1) >= 0 { if pDot(m0, n1) >= 0 { // n1 is between 0 and 45 degrees counter-clockwise of n0. s = n0 } else { // n1 is between 45 and 90 degrees counter-clockwise of n0. p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) s = pNeg(m2) } } else { pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8) p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) p.Add2(pm1.Add(n0t), pm1) if pDot(m2, n1) <= 0 { // n1 is between 90 and 135 degrees counter-clockwise of n0. s = pNeg(m1) } else { // n1 is between 135 and 180 degrees counter-clockwise of n0. p.Add2(pm1.Sub(n0t), pivot.Sub(m0)) s = pNeg(m0) } } } // The final quadratic segment has two endpoints s and n1 and the middle // control point is a multiple of s.Add(n1), i.e. it is on the angle // bisector of those two points. The multiple ranges between 128/256 and // 150/256 as the angle between s and n1 ranges between 0 and 45 degrees. // // When the angle is 0 degrees (i.e. s and n1 are coincident) then // s.Add(n1) is twice s and so the middle control point of the degenerate // quadratic segment should be half s.Add(n1), and half = 128/256. // // When the angle is 45 degrees then 150/256 is the ratio of the lengths of // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}. // // d is the normalized dot product between s and n1. Since the angle ranges // between 0 and 45 degrees then d ranges between 256/256 and 181/256. d := 256 * pDot(s, n1) / r2 multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2 p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1)) }
// interpolate returns the point (1-t)*a + t*b. func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 { s := 1<<12 - t x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X) y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y) return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)} }