func generateAtlas(font *truetype.Font, scale int32, dpi float64, width, height float32) ([]Vector4, *image.RGBA, []float32) { var low rune = 32 var high rune = 127 glyphCount := int32(high - low + 1) offsets := make([]float32, glyphCount) bounds := font.Bounds(scale) gw := float32(bounds.XMax - bounds.XMin) gh := float32(bounds.YMax - bounds.YMin) imageWidth := glh.Pow2(uint32(gw * float32(glyphCount))) imageHeight := glh.Pow2(uint32(gh)) imageBounds := image.Rect(0, 0, int(imageWidth), int(imageHeight)) sx := float32(2) / width sy := float32(2) / height w := gw * sx h := gh * sy img := image.NewRGBA(imageBounds) c := freetype.NewContext() c.SetDst(img) c.SetClip(img.Bounds()) c.SetSrc(image.White) c.SetDPI(dpi) c.SetFontSize(float64(scale)) c.SetFont(font) var gi int32 var gx, gy float32 verts := make([]Vector4, 0) texWidth := float32(img.Bounds().Dx()) texHeight := float32(img.Bounds().Dy()) for ch := low; ch <= high; ch++ { index := font.Index(ch) metric := font.HMetric(scale, index) //the offset is used when drawing a string of glyphs - we will advance a glyph's quad by the width of all previous glyphs in the string offsets[gi] = float32(metric.AdvanceWidth) * sx //draw the glyph into the atlas at the correct location pt := freetype.Pt(int(gx), int(gy)+int(c.PointToFix32(float64(scale))>>8)) c.DrawString(string(ch), pt) tx1 := gx / texWidth ty1 := gy / texHeight tx2 := (gx + gw) / texWidth ty2 := (gy + gh) / texHeight //the x,y coordinates are the same for each quad; only the texture coordinates (stored in z,w) change. //an optimization would be to only store texture coords, but I haven't figured that out yet verts = append(verts, Vector4{-1, 1, tx1, ty1}, Vector4{-1 + (w), 1, tx2, ty1}, Vector4{-1, 1 - (h), tx1, ty2}, Vector4{-1 + (w), 1 - (h), tx2, ty2}) gx += gw gi++ } return verts, img, offsets }
// LoadTruetype loads a truetype font from the given stream and // applies the given font scale in points. // // The low and high values determine the lower and upper rune limits // we should load for this font. For standard ASCII this would be: 32, 127. // // The dir value determines the orientation of the text we render // with this font. This should be any of the predefined Direction constants. func LoadTruetype(r io.Reader, scale int32, low, high rune, dir Direction) (*Font, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, err } // Read the truetype font. ttf, err := truetype.Parse(data) if err != nil { return nil, err } // Create our FontConfig type. var fc FontConfig fc.Dir = dir fc.Low = low fc.High = high fc.Glyphs = make(Charset, high-low+1) // Create an image, large enough to store all requested glyphs. // // We limit the image to 16 glyphs per row. Then add as many rows as // needed to encompass all glyphs, while making sure the resulting image // has power-of-two dimensions. gc := int32(len(fc.Glyphs)) glyphsPerRow := int32(16) glyphsPerCol := (gc / glyphsPerRow) + 1 gb := ttf.Bounds(scale) gw := (gb.XMax - gb.XMin) gh := (gb.YMax - gb.YMin) + 5 iw := glh.Pow2(uint32(gw * glyphsPerRow)) ih := glh.Pow2(uint32(gh * glyphsPerCol)) rect := image.Rect(0, 0, int(iw), int(ih)) img := image.NewRGBA(rect) // Use a freetype context to do the drawing. c := freetype.NewContext() c.SetDPI(72) c.SetFont(ttf) c.SetFontSize(float64(scale)) c.SetClip(img.Bounds()) c.SetDst(img) c.SetSrc(image.White) // Iterate over all relevant glyphs in the truetype font and // draw them all to the image buffer. // // For each glyph, we also create a corresponding Glyph structure // for our Charset. It contains the appropriate glyph coordinate offsets. var gi int var gx, gy int32 for ch := low; ch <= high; ch++ { index := ttf.Index(ch) metric := ttf.HMetric(scale, index) fc.Glyphs[gi].Advance = int(metric.AdvanceWidth) fc.Glyphs[gi].X = int(gx) fc.Glyphs[gi].Y = int(gy) fc.Glyphs[gi].Width = int(gw) fc.Glyphs[gi].Height = int(gh) pt := freetype.Pt(int(gx), int(gy)+int(c.PointToFix32(float64(scale))>>8)) c.DrawString(string(ch), pt) if gi%16 == 0 { gx = 0 gy += gh } else { gx += gw } gi++ } return loadFont(img, &fc) }