From f42630ca7acd3d7156d9d1f1a0eb754042eb4242 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Thu, 20 Aug 2015 16:07:51 +1000 Subject: [PATCH] Use the common golang.org/x/exp/shiny/font.Hinting type. --- example/freetype/main.go | 15 ++++++----- example/gamma/main.go | 6 ++--- example/raster/main.go | 6 ++--- example/round/main.go | 6 ++--- example/truetype/main.go | 17 ++++++------ freetype.go | 37 ++++++++++----------------- freetype_test.go | 4 +-- truetype/glyph.go | 33 +++++++++--------------- truetype/truetype_test.go | 54 ++++++++++++++++++--------------------- 9 files changed, 79 insertions(+), 99 deletions(-) diff --git a/example/freetype/main.go b/example/freetype/main.go index d5e60f9..33cef58 100644 --- a/example/freetype/main.go +++ b/example/freetype/main.go @@ -23,6 +23,7 @@ import ( "os" "github.com/golang/freetype" + "golang.org/x/exp/shiny/font" ) var ( @@ -80,7 +81,7 @@ func main() { log.Println(err) return } - font, err := freetype.ParseFont(fontBytes) + f, err := freetype.ParseFont(fontBytes) if err != nil { log.Println(err) return @@ -97,16 +98,16 @@ func main() { draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) c := freetype.NewContext() c.SetDPI(*dpi) - c.SetFont(font) + c.SetFont(f) c.SetFontSize(*size) c.SetClip(rgba.Bounds()) c.SetDst(rgba) c.SetSrc(fg) switch *hinting { default: - c.SetHinting(freetype.NoHinting) + c.SetHinting(font.HintingNone) case "full": - c.SetHinting(freetype.FullHinting) + c.SetHinting(font.HintingFull) } // Draw the guidelines. @@ -127,13 +128,13 @@ func main() { } // Save that RGBA image to disk. - f, err := os.Create("out.png") + outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } - defer f.Close() - b := bufio.NewWriter(f) + defer outFile.Close() + b := bufio.NewWriter(outFile) err = png.Encode(b, rgba) if err != nil { log.Println(err) diff --git a/example/gamma/main.go b/example/gamma/main.go index cc852ba..778697f 100644 --- a/example/gamma/main.go +++ b/example/gamma/main.go @@ -64,13 +64,13 @@ func main() { } // Save that RGBA image to disk. - f, err := os.Create("out.png") + outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } - defer f.Close() - b := bufio.NewWriter(f) + defer outFile.Close() + b := bufio.NewWriter(outFile) err = png.Encode(b, rgba) if err != nil { log.Println(err) diff --git a/example/raster/main.go b/example/raster/main.go index f55a3c8..ae9c57f 100644 --- a/example/raster/main.go +++ b/example/raster/main.go @@ -163,13 +163,13 @@ func main() { showNodes(rgba, inside) // Save that RGBA image to disk. - f, err := os.Create("out.png") + outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } - defer f.Close() - b := bufio.NewWriter(f) + defer outFile.Close() + b := bufio.NewWriter(outFile) err = png.Encode(b, rgba) if err != nil { log.Println(err) diff --git a/example/round/main.go b/example/round/main.go index c48cad3..6c3012e 100644 --- a/example/round/main.go +++ b/example/round/main.go @@ -88,13 +88,13 @@ func main() { } // Save that RGBA image to disk. - f, err := os.Create("out.png") + outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } - defer f.Close() - b := bufio.NewWriter(f) + defer outFile.Close() + b := bufio.NewWriter(outFile) err = png.Encode(b, m) if err != nil { log.Println(err) diff --git a/example/truetype/main.go b/example/truetype/main.go index 93ea5d0..ff9d27f 100644 --- a/example/truetype/main.go +++ b/example/truetype/main.go @@ -17,6 +17,7 @@ import ( "log" "github.com/golang/freetype/truetype" + "golang.org/x/exp/shiny/font" "golang.org/x/image/math/fixed" ) @@ -52,21 +53,21 @@ func main() { log.Println(err) return } - font, err := truetype.Parse(b) + f, err := truetype.Parse(b) if err != nil { log.Println(err) return } - fupe := fixed.Int26_6(font.FUnitsPerEm()) - printBounds(font.Bounds(fupe)) + fupe := fixed.Int26_6(f.FUnitsPerEm()) + printBounds(f.Bounds(fupe)) fmt.Printf("FUnitsPerEm:%d\n\n", fupe) c0, c1 := 'A', 'V' - i0 := font.Index(c0) - hm := font.HMetric(fupe, i0) + i0 := f.Index(c0) + hm := f.HMetric(fupe, i0) g := truetype.NewGlyphBuf() - err = g.Load(font, fupe, i0, truetype.NoHinting) + err = g.Load(f, fupe, i0, font.HintingNone) if err != nil { log.Println(err) return @@ -74,6 +75,6 @@ func main() { fmt.Printf("'%c' glyph\n", c0) fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing) printGlyph(g) - i1 := font.Index(c1) - fmt.Printf("\n'%c', '%c' Kerning:%d\n", c0, c1, font.Kerning(fupe, i0, i1)) + i1 := f.Index(c1) + fmt.Printf("\n'%c', '%c' Kerning:%d\n", c0, c1, f.Kerning(fupe, i0, i1)) } diff --git a/freetype.go b/freetype.go index 032ccfe..d871168 100644 --- a/freetype.go +++ b/freetype.go @@ -15,6 +15,7 @@ import ( "github.com/golang/freetype/raster" "github.com/golang/freetype/truetype" + "golang.org/x/exp/shiny/font" "golang.org/x/image/math/fixed" ) @@ -55,20 +56,10 @@ func Pt(x, y int) fixed.Point26_6 { } } -// Hinting is the policy for snapping a glyph's contours to pixel boundaries. -type Hinting int32 - -const ( - // NoHinting means to not perform any hinting. - NoHinting = Hinting(truetype.NoHinting) - // FullHinting means to use the font's hinting instructions. - FullHinting = Hinting(truetype.FullHinting) -) - // A Context holds the state for drawing text in a given font and size. type Context struct { r *raster.Rasterizer - font *truetype.Font + f *truetype.Font glyphBuf *truetype.GlyphBuf // clip is the clip rectangle for drawing. clip image.Rectangle @@ -79,7 +70,7 @@ type Context struct { // 26.6 fixed point units in 1 em. hinting is the hinting policy. fontSize, dpi float64 scale fixed.Int26_6 - hinting Hinting + hinting font.Hinting // cache is the glyph cache. cache [nGlyphs * nXFractions * nYFractions]cacheEntry } @@ -170,7 +161,7 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) { 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.font, c.scale, glyph, truetype.Hinting(c.hinting)); err != nil { + 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. @@ -237,15 +228,15 @@ func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) ( // // p is a fixed.Point26_6 and can therefore represent sub-pixel positions. func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) { - if c.font == nil { + if c.f == nil { return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font") } prev, hasPrev := truetype.Index(0), false for _, rune := range s { - index := c.font.Index(rune) + index := c.f.Index(rune) if hasPrev { - kern := fixed.Int26_6(c.font.Kerning(c.scale, prev, index)) - if c.hinting != NoHinting { + kern := fixed.Int26_6(c.f.Kerning(c.scale, prev, index)) + if c.hinting != font.HintingNone { kern = (kern + 32) &^ 63 } p.X += kern @@ -270,11 +261,11 @@ func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, erro // 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.font == nil { + 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.font.Bounds(c.scale) + b := c.f.Bounds(c.scale) xmin := +int(b.XMin) >> 6 ymin := -int(b.YMax) >> 6 xmax := +int(b.XMax+63) >> 6 @@ -296,11 +287,11 @@ func (c *Context) SetDPI(dpi float64) { } // SetFont sets the font used to draw text. -func (c *Context) SetFont(font *truetype.Font) { - if c.font == font { +func (c *Context) SetFont(f *truetype.Font) { + if c.f == f { return } - c.font = font + c.f = f c.recalc() } @@ -314,7 +305,7 @@ func (c *Context) SetFontSize(fontSize float64) { } // SetHinting sets the hinting policy. -func (c *Context) SetHinting(hinting Hinting) { +func (c *Context) SetHinting(hinting font.Hinting) { c.hinting = hinting for i := range c.cache { c.cache[i] = cacheEntry{} diff --git a/freetype_test.go b/freetype_test.go index 39f56b3..fef08b8 100644 --- a/freetype_test.go +++ b/freetype_test.go @@ -25,7 +25,7 @@ func BenchmarkDrawString(b *testing.B) { if err != nil { b.Fatal(err) } - font, err := ParseFont(data) + f, err := ParseFont(data) if err != nil { b.Fatal(err) } @@ -37,7 +37,7 @@ func BenchmarkDrawString(b *testing.B) { c.SetDst(dst) c.SetClip(dst.Bounds()) c.SetSrc(image.Black) - c.SetFont(font) + c.SetFont(f) var ms runtime.MemStats runtime.ReadMemStats(&ms) diff --git a/truetype/glyph.go b/truetype/glyph.go index 4735a66..1d549de 100644 --- a/truetype/glyph.go +++ b/truetype/glyph.go @@ -6,20 +6,11 @@ package truetype import ( + "golang.org/x/exp/shiny/font" "golang.org/x/image/math/fixed" ) -// Hinting is the policy for snapping a glyph's contours to pixel boundaries. -type Hinting uint32 - -const ( - // NoHinting means to not perform any hinting. - NoHinting Hinting = iota - // FullHinting means to use the font's hinting instructions. - FullHinting - - // TODO: implement VerticalHinting. -) +// TODO: implement VerticalHinting. // A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off' // control point. @@ -50,7 +41,7 @@ type GlyphBuf struct { font *Font scale fixed.Int26_6 - hinting Hinting + hinting font.Hinting hinter hinter // phantomPoints are the co-ordinates of the synthetic phantom points // used for hinting and bounding box calculations. @@ -91,7 +82,7 @@ const ( // 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 Hinting) error { +func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error { g.Point = g.Point[:0] g.Unhinted = g.Unhinted[:0] g.InFontUnits = g.InFontUnits[:0] @@ -103,7 +94,7 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h Hinting) error g.phantomPoints = [4]Point{} g.metricsSet = false - if h != NoHinting { + if h != font.HintingNone { if err := g.hinter.init(f, scale); err != nil { return err } @@ -115,7 +106,7 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h Hinting) error // 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 != NoHinting { + if h != font.HintingNone { pp1x = g.phantomPoints[0].X } if pp1x != 0 { @@ -125,7 +116,7 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h Hinting) error } advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X - if h != NoHinting { + 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:] { @@ -167,7 +158,7 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h Hinting) error } } // Snap the box to the grid, if hinting is on. - if h != NoHinting { + if h != font.HintingNone { g.B.XMin &^= 63 g.B.YMin &^= 63 g.B.XMax += 63 @@ -237,7 +228,7 @@ func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error program := g.loadSimple(glyf, ne) g.addPhantomsAndScale(np0, np0, true, true) pp1x = g.Point[len(g.Point)-4].X - if g.hinting != NoHinting { + if g.hinting != font.HintingNone { if len(program) != 0 { err := g.hinter.run( program, @@ -443,7 +434,7 @@ func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index, } instrLen := 0 - if g.hinting != NoHinting && offset+2 <= len(glyf) { + if g.hinting != font.HintingNone && offset+2 <= len(glyf) { instrLen = int(u16(glyf, offset)) offset += 2 } @@ -491,7 +482,7 @@ func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) { // Add the four phantom points. g.Point = append(g.Point, g.phantomPoints[:]...) // Scale the points. - if simple && g.hinting != NoHinting { + if simple && g.hinting != font.HintingNone { g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...) } for i := np1; i < len(g.Point); i++ { @@ -499,7 +490,7 @@ func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) { p.X = g.font.scale(g.scale * p.X) p.Y = g.font.scale(g.scale * p.Y) } - if g.hinting == NoHinting { + if g.hinting == font.HintingNone { return } // Round the 1st phantom point to the grid, shifting all other points equally. diff --git a/truetype/truetype_test.go b/truetype/truetype_test.go index 568bec6..06b2d5d 100644 --- a/truetype/truetype_test.go +++ b/truetype/truetype_test.go @@ -15,55 +15,56 @@ import ( "strings" "testing" + "golang.org/x/exp/shiny/font" "golang.org/x/image/math/fixed" ) -func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) { +func parseTestdataFont(name string) (f *Font, testdataIsOptional bool, err error) { b, err := ioutil.ReadFile(fmt.Sprintf("../testdata/%s.ttf", name)) if err != nil { // The "x-foo" fonts are optional tests, as they are not checked // in for copyright or file size reasons. return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err) } - font, err = Parse(b) + f, err = Parse(b) if err != nil { return nil, true, fmt.Errorf("%s: Parse: %v", name, err) } - return font, false, nil + return f, false, nil } // 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) { - font, _, err := parseTestdataFont("luxisr") + f, _, err := parseTestdataFont("luxisr") if err != nil { t.Fatal(err) } - if got, want := font.FUnitsPerEm(), int32(2048); got != want { + if got, want := f.FUnitsPerEm(), int32(2048); got != want { t.Errorf("FUnitsPerEm: got %v, want %v", got, want) } - fupe := fixed.Int26_6(font.FUnitsPerEm()) - if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want { + fupe := fixed.Int26_6(f.FUnitsPerEm()) + if got, want := f.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want { t.Errorf("Bounds: got %v, want %v", got, want) } - i0 := font.Index('A') - i1 := font.Index('V') + 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 := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want { + if got, want := f.HMetric(fupe, i0), (HMetric{1366, 19}); got != want { t.Errorf("HMetric: got %v, want %v", got, want) } - if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); 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 := font.Kerning(fupe, i0, i1), fixed.Int26_6(-144); got != want { + if got, want := f.Kerning(fupe, i0, i1), fixed.Int26_6(-144); got != want { t.Errorf("Kerning: got %v, want %v", got, want) } g := NewGlyphBuf() - err = g.Load(font, fupe, i0, NoHinting) + err = g.Load(f, fupe, i0, font.HintingNone) if err != nil { t.Fatalf("Load: %v", err) } @@ -180,7 +181,7 @@ func TestIndex(t *testing.T) { }, } for name, wants := range testCases { - font, testdataIsOptional, err := parseTestdataFont(name) + f, testdataIsOptional, err := parseTestdataFont(name) if err != nil { if testdataIsOptional { t.Log(err) @@ -190,7 +191,7 @@ func TestIndex(t *testing.T) { continue } for r, want := range wants { - if got := font.Index(r); got != want { + if got := f.Index(r); got != want { t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want) } } @@ -268,9 +269,9 @@ var scalingTestCases = []struct { {"x-times-new-roman", 13}, } -func testScaling(t *testing.T, h Hinting) { +func testScaling(t *testing.T, h font.Hinting) { for _, tc := range scalingTestCases { - font, testdataIsOptional, err := parseTestdataFont(tc.name) + f, testdataIsOptional, err := parseTestdataFont(tc.name) if err != nil { if testdataIsOptional { t.Log(err) @@ -280,19 +281,19 @@ func testScaling(t *testing.T, h Hinting) { continue } hintingStr := "sans" - if h != NoHinting { + if h != font.HintingNone { hintingStr = "with" } - f, err := os.Open(fmt.Sprintf( + testFile, err := os.Open(fmt.Sprintf( "../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr)) if err != nil { t.Errorf("%s: Open: %v", tc.name, err) continue } - defer f.Close() + defer testFile.Close() wants := []scalingTestData{} - scanner := bufio.NewScanner(f) + scanner := bufio.NewScanner(testFile) if scanner.Scan() { major, minor, patch := 0, 0, 0 _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch) @@ -320,7 +321,7 @@ func testScaling(t *testing.T, h Hinting) { glyphBuf := NewGlyphBuf() for i, want := range wants { - if err = glyphBuf.Load(font, fixed.I(tc.size), Index(i), h); err != nil { + if err = glyphBuf.Load(f, fixed.I(tc.size), Index(i), h); err != nil { t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err) continue } @@ -359,10 +360,5 @@ func testScaling(t *testing.T, h Hinting) { } } -func TestScalingSansHinting(t *testing.T) { - testScaling(t, NoHinting) -} - -func TestScalingWithHinting(t *testing.T) { - testScaling(t, FullHinting) -} +func TestScalingHintingNone(t *testing.T) { testScaling(t, font.HintingNone) } +func TestScalingHintingFull(t *testing.T) { testScaling(t, font.HintingFull) }