...

Source file src/github.com/golang/freetype/freetype.go

Documentation: github.com/golang/freetype

     1  // Copyright 2010 The Freetype-Go Authors. All rights reserved.
     2  // Use of this source code is governed by your choice of either the
     3  // FreeType License or the GNU General Public License version 2 (or
     4  // any later version), both of which can be found in the LICENSE file.
     5  
     6  // The freetype package provides a convenient API to draw text onto an image.
     7  // Use the freetype/raster and freetype/truetype packages for lower level
     8  // control over rasterization and TrueType parsing.
     9  package freetype // import "github.com/golang/freetype"
    10  
    11  import (
    12  	"errors"
    13  	"image"
    14  	"image/draw"
    15  
    16  	"github.com/golang/freetype/raster"
    17  	"github.com/golang/freetype/truetype"
    18  	"golang.org/x/image/font"
    19  	"golang.org/x/image/math/fixed"
    20  )
    21  
    22  // These constants determine the size of the glyph cache. The cache is keyed
    23  // primarily by the glyph index modulo nGlyphs, and secondarily by sub-pixel
    24  // position for the mask image. Sub-pixel positions are quantized to
    25  // nXFractions possible values in both the x and y directions.
    26  const (
    27  	nGlyphs     = 256
    28  	nXFractions = 4
    29  	nYFractions = 1
    30  )
    31  
    32  // An entry in the glyph cache is keyed explicitly by the glyph index and
    33  // implicitly by the quantized x and y fractional offset. It maps to a mask
    34  // image and an offset.
    35  type cacheEntry struct {
    36  	valid        bool
    37  	glyph        truetype.Index
    38  	advanceWidth fixed.Int26_6
    39  	mask         *image.Alpha
    40  	offset       image.Point
    41  }
    42  
    43  // ParseFont just calls the Parse function from the freetype/truetype package.
    44  // It is provided here so that code that imports this package doesn't need
    45  // to also include the freetype/truetype package.
    46  func ParseFont(b []byte) (*truetype.Font, error) {
    47  	return truetype.Parse(b)
    48  }
    49  
    50  // Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6
    51  // co-ordinate pair measured in fixed.Int26_6 units.
    52  func Pt(x, y int) fixed.Point26_6 {
    53  	return fixed.Point26_6{
    54  		X: fixed.Int26_6(x << 6),
    55  		Y: fixed.Int26_6(y << 6),
    56  	}
    57  }
    58  
    59  // A Context holds the state for drawing text in a given font and size.
    60  type Context struct {
    61  	r        *raster.Rasterizer
    62  	f        *truetype.Font
    63  	glyphBuf truetype.GlyphBuf
    64  	// clip is the clip rectangle for drawing.
    65  	clip image.Rectangle
    66  	// dst and src are the destination and source images for drawing.
    67  	dst draw.Image
    68  	src image.Image
    69  	// fontSize and dpi are used to calculate scale. scale is the number of
    70  	// 26.6 fixed point units in 1 em. hinting is the hinting policy.
    71  	fontSize, dpi float64
    72  	scale         fixed.Int26_6
    73  	hinting       font.Hinting
    74  	// cache is the glyph cache.
    75  	cache [nGlyphs * nXFractions * nYFractions]cacheEntry
    76  }
    77  
    78  // PointToFixed converts the given number of points (as in "a 12 point font")
    79  // into a 26.6 fixed point number of pixels.
    80  func (c *Context) PointToFixed(x float64) fixed.Int26_6 {
    81  	return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
    82  }
    83  
    84  // drawContour draws the given closed contour with the given offset.
    85  func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) {
    86  	if len(ps) == 0 {
    87  		return
    88  	}
    89  
    90  	// The low bit of each point's Flags value is whether the point is on the
    91  	// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
    92  	// Thus, two consecutive off-curve points imply an on-curve point in the
    93  	// middle of those two.
    94  	//
    95  	// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
    96  
    97  	// ps[0] is a truetype.Point measured in FUnits and positive Y going
    98  	// upwards. start is the same thing measured in fixed point units and
    99  	// positive Y going downwards, and offset by (dx, dy).
   100  	start := fixed.Point26_6{
   101  		X: dx + ps[0].X,
   102  		Y: dy - ps[0].Y,
   103  	}
   104  	others := []truetype.Point(nil)
   105  	if ps[0].Flags&0x01 != 0 {
   106  		others = ps[1:]
   107  	} else {
   108  		last := fixed.Point26_6{
   109  			X: dx + ps[len(ps)-1].X,
   110  			Y: dy - ps[len(ps)-1].Y,
   111  		}
   112  		if ps[len(ps)-1].Flags&0x01 != 0 {
   113  			start = last
   114  			others = ps[:len(ps)-1]
   115  		} else {
   116  			start = fixed.Point26_6{
   117  				X: (start.X + last.X) / 2,
   118  				Y: (start.Y + last.Y) / 2,
   119  			}
   120  			others = ps
   121  		}
   122  	}
   123  	c.r.Start(start)
   124  	q0, on0 := start, true
   125  	for _, p := range others {
   126  		q := fixed.Point26_6{
   127  			X: dx + p.X,
   128  			Y: dy - p.Y,
   129  		}
   130  		on := p.Flags&0x01 != 0
   131  		if on {
   132  			if on0 {
   133  				c.r.Add1(q)
   134  			} else {
   135  				c.r.Add2(q0, q)
   136  			}
   137  		} else {
   138  			if on0 {
   139  				// No-op.
   140  			} else {
   141  				mid := fixed.Point26_6{
   142  					X: (q0.X + q.X) / 2,
   143  					Y: (q0.Y + q.Y) / 2,
   144  				}
   145  				c.r.Add2(q0, mid)
   146  			}
   147  		}
   148  		q0, on0 = q, on
   149  	}
   150  	// Close the curve.
   151  	if on0 {
   152  		c.r.Add1(start)
   153  	} else {
   154  		c.r.Add2(q0, start)
   155  	}
   156  }
   157  
   158  // rasterize returns the advance width, glyph mask and integer-pixel offset
   159  // to render the given glyph at the given sub-pixel offsets.
   160  // The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
   161  func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
   162  	fixed.Int26_6, *image.Alpha, image.Point, error) {
   163  
   164  	if err := c.glyphBuf.Load(c.f, c.scale, glyph, c.hinting); err != nil {
   165  		return 0, nil, image.Point{}, err
   166  	}
   167  	// Calculate the integer-pixel bounds for the glyph.
   168  	xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6
   169  	ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6
   170  	xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6
   171  	ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6
   172  	if xmin > xmax || ymin > ymax {
   173  		return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
   174  	}
   175  	// A TrueType's glyph's nodes can have negative co-ordinates, but the
   176  	// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
   177  	// the pixel offsets, based on the font's FUnit metrics, that let a
   178  	// negative co-ordinate in TrueType space be non-negative in rasterizer
   179  	// space. xmin and ymin are typically <= 0.
   180  	fx -= fixed.Int26_6(xmin << 6)
   181  	fy -= fixed.Int26_6(ymin << 6)
   182  	// Rasterize the glyph's vectors.
   183  	c.r.Clear()
   184  	e0 := 0
   185  	for _, e1 := range c.glyphBuf.Ends {
   186  		c.drawContour(c.glyphBuf.Points[e0:e1], fx, fy)
   187  		e0 = e1
   188  	}
   189  	a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
   190  	c.r.Rasterize(raster.NewAlphaSrcPainter(a))
   191  	return c.glyphBuf.AdvanceWidth, a, image.Point{xmin, ymin}, nil
   192  }
   193  
   194  // glyph returns the advance width, glyph mask and integer-pixel offset to
   195  // render the given glyph at the given sub-pixel point. It is a cache for the
   196  // rasterize method. Unlike rasterize, p's co-ordinates do not have to be in
   197  // the range [0, 1).
   198  func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) (
   199  	fixed.Int26_6, *image.Alpha, image.Point, error) {
   200  
   201  	// Split p.X and p.Y into their integer and fractional parts.
   202  	ix, fx := int(p.X>>6), p.X&0x3f
   203  	iy, fy := int(p.Y>>6), p.Y&0x3f
   204  	// Calculate the index t into the cache array.
   205  	tg := int(glyph) % nGlyphs
   206  	tx := int(fx) / (64 / nXFractions)
   207  	ty := int(fy) / (64 / nYFractions)
   208  	t := ((tg*nXFractions)+tx)*nYFractions + ty
   209  	// Check for a cache hit.
   210  	if e := c.cache[t]; e.valid && e.glyph == glyph {
   211  		return e.advanceWidth, e.mask, e.offset.Add(image.Point{ix, iy}), nil
   212  	}
   213  	// Rasterize the glyph and put the result into the cache.
   214  	advanceWidth, mask, offset, err := c.rasterize(glyph, fx, fy)
   215  	if err != nil {
   216  		return 0, nil, image.Point{}, err
   217  	}
   218  	c.cache[t] = cacheEntry{true, glyph, advanceWidth, mask, offset}
   219  	return advanceWidth, mask, offset.Add(image.Point{ix, iy}), nil
   220  }
   221  
   222  // DrawString draws s at p and returns p advanced by the text extent. The text
   223  // is placed so that the left edge of the em square of the first character of s
   224  // and the baseline intersect at p. The majority of the affected pixels will be
   225  // above and to the right of the point, but some may be below or to the left.
   226  // For example, drawing a string that starts with a 'J' in an italic font may
   227  // affect pixels below and left of the point.
   228  //
   229  // p is a fixed.Point26_6 and can therefore represent sub-pixel positions.
   230  func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) {
   231  	if c.f == nil {
   232  		return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font")
   233  	}
   234  	prev, hasPrev := truetype.Index(0), false
   235  	for _, rune := range s {
   236  		index := c.f.Index(rune)
   237  		if hasPrev {
   238  			kern := c.f.Kern(c.scale, prev, index)
   239  			if c.hinting != font.HintingNone {
   240  				kern = (kern + 32) &^ 63
   241  			}
   242  			p.X += kern
   243  		}
   244  		advanceWidth, mask, offset, err := c.glyph(index, p)
   245  		if err != nil {
   246  			return fixed.Point26_6{}, err
   247  		}
   248  		p.X += advanceWidth
   249  		glyphRect := mask.Bounds().Add(offset)
   250  		dr := c.clip.Intersect(glyphRect)
   251  		if !dr.Empty() {
   252  			mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y}
   253  			draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over)
   254  		}
   255  		prev, hasPrev = index, true
   256  	}
   257  	return p, nil
   258  }
   259  
   260  // recalc recalculates scale and bounds values from the font size, screen
   261  // resolution and font metrics, and invalidates the glyph cache.
   262  func (c *Context) recalc() {
   263  	c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0))
   264  	if c.f == nil {
   265  		c.r.SetBounds(0, 0)
   266  	} else {
   267  		// Set the rasterizer's bounds to be big enough to handle the largest glyph.
   268  		b := c.f.Bounds(c.scale)
   269  		xmin := +int(b.Min.X) >> 6
   270  		ymin := -int(b.Max.Y) >> 6
   271  		xmax := +int(b.Max.X+63) >> 6
   272  		ymax := -int(b.Min.Y-63) >> 6
   273  		c.r.SetBounds(xmax-xmin, ymax-ymin)
   274  	}
   275  	for i := range c.cache {
   276  		c.cache[i] = cacheEntry{}
   277  	}
   278  }
   279  
   280  // SetDPI sets the screen resolution in dots per inch.
   281  func (c *Context) SetDPI(dpi float64) {
   282  	if c.dpi == dpi {
   283  		return
   284  	}
   285  	c.dpi = dpi
   286  	c.recalc()
   287  }
   288  
   289  // SetFont sets the font used to draw text.
   290  func (c *Context) SetFont(f *truetype.Font) {
   291  	if c.f == f {
   292  		return
   293  	}
   294  	c.f = f
   295  	c.recalc()
   296  }
   297  
   298  // SetFontSize sets the font size in points (as in "a 12 point font").
   299  func (c *Context) SetFontSize(fontSize float64) {
   300  	if c.fontSize == fontSize {
   301  		return
   302  	}
   303  	c.fontSize = fontSize
   304  	c.recalc()
   305  }
   306  
   307  // SetHinting sets the hinting policy.
   308  func (c *Context) SetHinting(hinting font.Hinting) {
   309  	c.hinting = hinting
   310  	for i := range c.cache {
   311  		c.cache[i] = cacheEntry{}
   312  	}
   313  }
   314  
   315  // SetDst sets the destination image for draw operations.
   316  func (c *Context) SetDst(dst draw.Image) {
   317  	c.dst = dst
   318  }
   319  
   320  // SetSrc sets the source image for draw operations. This is typically an
   321  // image.Uniform.
   322  func (c *Context) SetSrc(src image.Image) {
   323  	c.src = src
   324  }
   325  
   326  // SetClip sets the clip rectangle for drawing.
   327  func (c *Context) SetClip(clip image.Rectangle) {
   328  	c.clip = clip
   329  }
   330  
   331  // TODO(nigeltao): implement Context.SetGamma.
   332  
   333  // NewContext creates a new Context.
   334  func NewContext() *Context {
   335  	return &Context{
   336  		r:        raster.NewRasterizer(0, 0),
   337  		fontSize: 12,
   338  		dpi:      72,
   339  		scale:    12 << 6,
   340  	}
   341  }
   342  

View as plain text