face.go 12.5 KB
Newer Older
N
Nigel Tao 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright 2015 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.

package truetype

import (
	"image"

	"github.com/golang/freetype/raster"
	"golang.org/x/exp/shiny/font"
	"golang.org/x/image/math/fixed"
)

N
Nigel Tao 已提交
16 17 18 19
func powerOf2(i int) bool {
	return i != 0 && (i&(i-1)) == 0
}

N
Nigel Tao 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// Options are optional arguments to NewFace.
type Options struct {
	// Size is the font size in points, as in "a 10 point font size".
	//
	// A zero value means to use a 12 point font size.
	Size float64

	// DPI is the dots-per-inch resolution.
	//
	// A zero value means to use 72 DPI.
	DPI float64

	// Hinting is how to quantize the glyph nodes.
	//
	// A zero value means to use no hinting.
	Hinting font.Hinting
N
Nigel Tao 已提交
36

N
Nigel Tao 已提交
37 38 39 40 41 42 43 44
	// GlyphCacheEntries is the number of entries in the glyph mask image
	// cache.
	//
	// If non-zero, it must be a power of 2.
	//
	// A zero value means to use 512 entries.
	GlyphCacheEntries int

N
Nigel Tao 已提交
45 46 47 48 49 50 51
	// SubPixelsX is the number of sub-pixel locations a glyph's dot is
	// quantized to, in the horizontal direction. For example, a value of 8
	// means that the dot is quantized to 1/8th of a pixel. This quantization
	// only affects the glyph mask image, not its bounding box or advance
	// width. A higher value gives a more faithful glyph image, but reduces the
	// effectiveness of the glyph cache.
	//
N
Nigel Tao 已提交
52
	// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
N
Nigel Tao 已提交
53 54 55 56 57 58 59 60 61 62 63
	//
	// A zero value means to use 4 sub-pixel locations.
	SubPixelsX int

	// SubPixelsY is the number of sub-pixel locations a glyph's dot is
	// quantized to, in the vertical direction. For example, a value of 8
	// means that the dot is quantized to 1/8th of a pixel. This quantization
	// only affects the glyph mask image, not its bounding box or advance
	// width. A higher value gives a more faithful glyph image, but reduces the
	// effectiveness of the glyph cache.
	//
N
Nigel Tao 已提交
64
	// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
N
Nigel Tao 已提交
65 66 67
	//
	// A zero value means to use 1 sub-pixel location.
	SubPixelsY int
N
Nigel Tao 已提交
68 69 70
}

func (o *Options) size() float64 {
N
Nigel Tao 已提交
71
	if o != nil && o.Size > 0 {
N
Nigel Tao 已提交
72 73 74 75 76 77
		return o.Size
	}
	return 12
}

func (o *Options) dpi() float64 {
N
Nigel Tao 已提交
78
	if o != nil && o.DPI > 0 {
N
Nigel Tao 已提交
79 80 81 82 83 84
		return o.DPI
	}
	return 72
}

func (o *Options) hinting() font.Hinting {
N
Nigel Tao 已提交
85 86 87 88 89 90
	if o != nil {
		switch o.Hinting {
		case font.HintingVertical, font.HintingFull:
			// TODO: support vertical hinting.
			return font.HintingFull
		}
N
Nigel Tao 已提交
91 92 93 94
	}
	return font.HintingNone
}

N
Nigel Tao 已提交
95 96 97 98 99 100 101 102 103 104
func (o *Options) glyphCacheEntries() int {
	if o != nil && powerOf2(o.GlyphCacheEntries) {
		return o.GlyphCacheEntries
	}
	// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
	// locations in the X and Y direction.
	return 512
}

func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
N
Nigel Tao 已提交
105 106 107 108 109 110 111 112 113 114 115 116
	if o != nil {
		switch o.SubPixelsX {
		case 1, 2, 4, 8, 16, 32, 64:
			return subPixels(o.SubPixelsX)
		}
	}
	// This default value of 4 isn't based on anything scientific, merely as
	// small a number as possible that looks almost as good as no quantization,
	// or returning subPixels(64).
	return subPixels(4)
}

N
Nigel Tao 已提交
117
func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
N
Nigel Tao 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130
	if o != nil {
		switch o.SubPixelsX {
		case 1, 2, 4, 8, 16, 32, 64:
			return subPixels(o.SubPixelsX)
		}
	}
	// This default value of 1 isn't based on anything scientific, merely that
	// vertical sub-pixel glyph rendering is pretty rare. Baseline locations
	// can usually afford to snap to the pixel grid, so the vertical direction
	// doesn't have the deal with the horizontal's fractional advance widths.
	return subPixels(1)
}

N
Nigel Tao 已提交
131 132
// subPixels returns q and the bias and mask that leads to q quantized
// sub-pixel locations per full pixel.
N
Nigel Tao 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
//
// 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.
N
Nigel Tao 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
	return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
}

// cacheEntry caches the arguments and return values of rasterize.
type cacheEntry struct {
	key cacheKey
	val cacheVal
}

type cacheKey struct {
	index  Index
	fx, fy uint8
}

type cacheVal struct {
	advanceWidth fixed.Int26_6
	offset       image.Point
	gw           int
	gh           int
N
Nigel Tao 已提交
170 171
}

N
Nigel Tao 已提交
172
// NewFace returns a new font.Face for the given Font.
N
Nigel Tao 已提交
173
func NewFace(f *Font, opts *Options) font.Face {
N
Nigel Tao 已提交
174 175 176 177
	a := &face{
		f:       f,
		hinting: opts.hinting(),
		scale:   fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
N
Nigel Tao 已提交
178 179 180 181 182 183 184 185 186
		cache:   make([]cacheEntry, 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 cache entries have fx and fy
	// in the range [0, 64).
	for i := range a.cache {
		a.cache[i].key.fy = 0xff
N
Nigel Tao 已提交
187 188 189 190 191 192 193 194
	}

	// Set the rasterizer's bounds to be big enough to handle the largest glyph.
	b := f.Bounds(a.scale)
	xmin := +int(b.XMin) >> 6
	ymin := -int(b.YMax) >> 6
	xmax := +int(b.XMax+63) >> 6
	ymax := -int(b.YMin-63) >> 6
195 196
	a.maxw = xmax - xmin
	a.maxh = ymax - ymin
N
Nigel Tao 已提交
197
	a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.cache)))
198
	a.r.SetBounds(a.maxw, a.maxh)
N
Nigel Tao 已提交
199
	a.p = facePainter{a}
N
Nigel Tao 已提交
200 201 202 203 204

	return a
}

type face struct {
N
Nigel Tao 已提交
205 206 207
	f             *Font
	hinting       font.Hinting
	scale         fixed.Int26_6
N
Nigel Tao 已提交
208
	subPixelX     uint32
N
Nigel Tao 已提交
209 210
	subPixelBiasX fixed.Int26_6
	subPixelMaskX fixed.Int26_6
N
Nigel Tao 已提交
211
	subPixelY     uint32
N
Nigel Tao 已提交
212 213
	subPixelBiasY fixed.Int26_6
	subPixelMaskY fixed.Int26_6
N
Nigel Tao 已提交
214 215
	masks         *image.Alpha
	cache         []cacheEntry
N
Nigel Tao 已提交
216 217
	r             raster.Rasterizer
	p             raster.Painter
N
Nigel Tao 已提交
218
	paintOffset   int
N
Nigel Tao 已提交
219 220 221
	maxw          int
	maxh          int
	glyphBuf      GlyphBuf
N
Nigel Tao 已提交
222 223 224 225 226 227 228 229 230 231 232

	// TODO: clip rectangle?
}

// Close satisfies the font.Face interface.
func (a *face) Close() error { return nil }

// Kern satisfies the font.Face interface.
func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
	i0 := a.f.Index(r0)
	i1 := a.f.Index(r1)
N
Nigel Tao 已提交
233
	kern := a.f.Kern(a.scale, i0, i1)
N
Nigel Tao 已提交
234 235 236 237 238 239 240 241 242 243
	if a.hinting != font.HintingNone {
		kern = (kern + 32) &^ 63
	}
	return kern
}

// Glyph satisfies the font.Face interface.
func (a *face) Glyph(dot fixed.Point26_6, r rune) (
	newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool) {

N
Nigel Tao 已提交
244 245 246 247 248 249 250
	// Quantize to the sub-pixel granularity.
	dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
	dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY

	// Split the coordinates into their integer and fractional parts.
	ix, fx := int(dotX>>6), dotX&0x3f
	iy, fy := int(dotY>>6), dotY&0x3f
N
Nigel Tao 已提交
251

N
Nigel Tao 已提交
252 253 254 255 256 257 258 259 260 261
	index := a.f.Index(r)
	cIndex := uint32(index)
	cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
	cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
	cIndex &= uint32(len(a.cache) - 1)
	a.paintOffset = a.maxh * int(cIndex)
	k := cacheKey{
		index: index,
		fx:    uint8(fx),
		fy:    uint8(fy),
N
Nigel Tao 已提交
262
	}
N
Nigel Tao 已提交
263 264 265 266 267 268 269 270 271 272 273 274
	var v cacheVal
	if a.cache[cIndex].key != k {
		var ok bool
		v, ok = a.rasterize(index, fx, fy)
		if !ok {
			return fixed.Point26_6{}, image.Rectangle{}, nil, image.Point{}, false
		}
		a.cache[cIndex] = cacheEntry{k, v}
	} else {
		v = a.cache[cIndex].val
	}

N
Nigel Tao 已提交
275
	newDot = fixed.Point26_6{
N
Nigel Tao 已提交
276
		X: dot.X + v.advanceWidth,
N
Nigel Tao 已提交
277 278 279
		Y: dot.Y,
	}
	dr.Min = image.Point{
N
Nigel Tao 已提交
280 281
		X: ix + v.offset.X,
		Y: iy + v.offset.Y,
N
Nigel Tao 已提交
282 283
	}
	dr.Max = image.Point{
N
Nigel Tao 已提交
284 285
		X: dr.Min.X + v.gw,
		Y: dr.Min.Y + v.gh,
N
Nigel Tao 已提交
286
	}
N
Nigel Tao 已提交
287
	return newDot, dr, a.masks, image.Point{Y: a.paintOffset}, true
N
Nigel Tao 已提交
288 289
}

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
	if err := a.glyphBuf.Load(a.f, a.scale, a.f.Index(r), a.hinting); err != nil {
		return fixed.Rectangle26_6{}, 0, false
	}
	xmin := +a.glyphBuf.B.XMin
	ymin := -a.glyphBuf.B.YMax
	xmax := +a.glyphBuf.B.XMax
	ymax := -a.glyphBuf.B.YMin
	if xmin > xmax || ymin > ymax {
		return fixed.Rectangle26_6{}, 0, false
	}
	return fixed.Rectangle26_6{
		Min: fixed.Point26_6{
			X: xmin,
			Y: ymin,
		},
		Max: fixed.Point26_6{
			X: xmax,
			Y: ymax,
		},
	}, a.glyphBuf.AdvanceWidth, true
}

func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
	if err := a.glyphBuf.Load(a.f, a.scale, a.f.Index(r), a.hinting); err != nil {
		return 0, false
	}
	return a.glyphBuf.AdvanceWidth, true
}

320 321 322
// 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.
//
N
Nigel Tao 已提交
323
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
N
Nigel Tao 已提交
324
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v cacheVal, ok bool) {
N
Nigel Tao 已提交
325
	if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
N
Nigel Tao 已提交
326
		return cacheVal{}, false
N
Nigel Tao 已提交
327 328
	}
	// Calculate the integer-pixel bounds for the glyph.
329 330 331 332
	xmin := int(fx+a.glyphBuf.B.XMin) >> 6
	ymin := int(fy-a.glyphBuf.B.YMax) >> 6
	xmax := int(fx+a.glyphBuf.B.XMax+0x3f) >> 6
	ymax := int(fy-a.glyphBuf.B.YMin+0x3f) >> 6
N
Nigel Tao 已提交
333
	if xmin > xmax || ymin > ymax {
N
Nigel Tao 已提交
334
		return cacheVal{}, false
N
Nigel Tao 已提交
335 336 337 338 339 340
	}
	// 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.
341 342
	fx -= fixed.Int26_6(xmin << 6)
	fy -= fixed.Int26_6(ymin << 6)
N
Nigel Tao 已提交
343 344
	// Rasterize the glyph's vectors.
	a.r.Clear()
N
Nigel Tao 已提交
345 346
	pixOffset := a.paintOffset * a.maxw
	clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
N
Nigel Tao 已提交
347 348 349 350 351
	e0 := 0
	for _, e1 := range a.glyphBuf.End {
		a.drawContour(a.glyphBuf.Point[e0:e1], fx, fy)
		e0 = e1
	}
352
	a.r.Rasterize(a.p)
N
Nigel Tao 已提交
353 354 355 356 357 358
	return cacheVal{
		a.glyphBuf.AdvanceWidth,
		image.Point{xmin, ymin},
		xmax - xmin,
		ymax - ymin,
	}, true
359 360 361 362 363 364
}

func clear(pix []byte) {
	for i := range pix {
		pix[i] = 0
	}
N
Nigel Tao 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
}

// drawContour draws the given closed contour with the given offset.
func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
	if len(ps) == 0 {
		return
	}

	// The low bit of each point's Flags value is whether the point is on the
	// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
	// Thus, two consecutive off-curve points imply an on-curve point in the
	// middle of those two.
	//
	// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.

	// ps[0] is a truetype.Point measured in FUnits and positive Y going
	// upwards. start is the same thing measured in fixed point units and
	// positive Y going downwards, and offset by (dx, dy).
	start := fixed.Point26_6{
384 385
		X: dx + ps[0].X,
		Y: dy - ps[0].Y,
N
Nigel Tao 已提交
386 387 388 389 390 391
	}
	var others []Point
	if ps[0].Flags&0x01 != 0 {
		others = ps[1:]
	} else {
		last := fixed.Point26_6{
392 393
			X: dx + ps[len(ps)-1].X,
			Y: dy - ps[len(ps)-1].Y,
N
Nigel Tao 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
		}
		if ps[len(ps)-1].Flags&0x01 != 0 {
			start = last
			others = ps[:len(ps)-1]
		} else {
			start = fixed.Point26_6{
				X: (start.X + last.X) / 2,
				Y: (start.Y + last.Y) / 2,
			}
			others = ps
		}
	}
	a.r.Start(start)
	q0, on0 := start, true
	for _, p := range others {
		q := fixed.Point26_6{
410 411
			X: dx + p.X,
			Y: dy - p.Y,
N
Nigel Tao 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
		}
		on := p.Flags&0x01 != 0
		if on {
			if on0 {
				a.r.Add1(q)
			} else {
				a.r.Add2(q0, q)
			}
		} else {
			if on0 {
				// No-op.
			} else {
				mid := fixed.Point26_6{
					X: (q0.X + q.X) / 2,
					Y: (q0.Y + q.Y) / 2,
				}
				a.r.Add2(q0, mid)
			}
		}
		q0, on0 = q, on
	}
	// Close the curve.
	if on0 {
		a.r.Add1(start)
	} else {
		a.r.Add2(q0, start)
	}
}
N
Nigel Tao 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
// (face.paintOffset) to the painted spans.
type facePainter struct {
	a *face
}

func (p facePainter) Paint(ss []raster.Span, done bool) {
	m := p.a.masks
	b := m.Bounds()
	b.Min.Y = p.a.paintOffset
	b.Max.Y = p.a.paintOffset + p.a.maxh
	for _, s := range ss {
		s.Y += p.a.paintOffset
		if s.Y < b.Min.Y {
			continue
		}
		if s.Y >= b.Max.Y {
			return
		}
		if s.X0 < b.Min.X {
			s.X0 = b.Min.X
		}
		if s.X1 > b.Max.X {
			s.X1 = b.Max.X
		}
		if s.X0 >= s.X1 {
			continue
		}
		base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
		p := m.Pix[base+s.X0 : base+s.X1]
		color := uint8(s.A >> 24)
		for i := range p {
			p[i] = color
		}
	}
}