1// Copyright 2015 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 6package truetype 7 8import ( 9 "image" 10 "math" 11 12 "github.com/golang/freetype/raster" 13 "golang.org/x/image/font" 14 "golang.org/x/image/math/fixed" 15) 16 17func powerOf2(i int) bool { 18 return i != 0 && (i&(i-1)) == 0 19} 20 21// Options are optional arguments to NewFace. 22type Options struct { 23 // Size is the font size in points, as in "a 10 point font size". 24 // 25 // A zero value means to use a 12 point font size. 26 Size float64 27 28 // DPI is the dots-per-inch resolution. 29 // 30 // A zero value means to use 72 DPI. 31 DPI float64 32 33 // Hinting is how to quantize the glyph nodes. 34 // 35 // A zero value means to use no hinting. 36 Hinting font.Hinting 37 38 // GlyphCacheEntries is the number of entries in the glyph mask image 39 // cache. 40 // 41 // If non-zero, it must be a power of 2. 42 // 43 // A zero value means to use 512 entries. 44 GlyphCacheEntries int 45 46 // SubPixelsX is the number of sub-pixel locations a glyph's dot is 47 // quantized to, in the horizontal direction. For example, a value of 8 48 // means that the dot is quantized to 1/8th of a pixel. This quantization 49 // only affects the glyph mask image, not its bounding box or advance 50 // width. A higher value gives a more faithful glyph image, but reduces the 51 // effectiveness of the glyph cache. 52 // 53 // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive. 54 // 55 // A zero value means to use 4 sub-pixel locations. 56 SubPixelsX int 57 58 // SubPixelsY is the number of sub-pixel locations a glyph's dot is 59 // quantized to, in the vertical direction. For example, a value of 8 60 // means that the dot is quantized to 1/8th of a pixel. This quantization 61 // only affects the glyph mask image, not its bounding box or advance 62 // width. A higher value gives a more faithful glyph image, but reduces the 63 // effectiveness of the glyph cache. 64 // 65 // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive. 66 // 67 // A zero value means to use 1 sub-pixel location. 68 SubPixelsY int 69} 70 71func (o *Options) size() float64 { 72 if o != nil && o.Size > 0 { 73 return o.Size 74 } 75 return 12 76} 77 78func (o *Options) dpi() float64 { 79 if o != nil && o.DPI > 0 { 80 return o.DPI 81 } 82 return 72 83} 84 85func (o *Options) hinting() font.Hinting { 86 if o != nil { 87 switch o.Hinting { 88 case font.HintingVertical, font.HintingFull: 89 // TODO: support vertical hinting. 90 return font.HintingFull 91 } 92 } 93 return font.HintingNone 94} 95 96func (o *Options) glyphCacheEntries() int { 97 if o != nil && powerOf2(o.GlyphCacheEntries) { 98 return o.GlyphCacheEntries 99 } 100 // 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel 101 // locations in the X and Y direction. 102 return 512 103} 104 105func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) { 106 if o != nil { 107 switch o.SubPixelsX { 108 case 1, 2, 4, 8, 16, 32, 64: 109 return subPixels(o.SubPixelsX) 110 } 111 } 112 // This default value of 4 isn't based on anything scientific, merely as 113 // small a number as possible that looks almost as good as no quantization, 114 // or returning subPixels(64). 115 return subPixels(4) 116} 117 118func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) { 119 if o != nil { 120 switch o.SubPixelsX { 121 case 1, 2, 4, 8, 16, 32, 64: 122 return subPixels(o.SubPixelsX) 123 } 124 } 125 // This default value of 1 isn't based on anything scientific, merely that 126 // vertical sub-pixel glyph rendering is pretty rare. Baseline locations 127 // can usually afford to snap to the pixel grid, so the vertical direction 128 // doesn't have the deal with the horizontal's fractional advance widths. 129 return subPixels(1) 130} 131 132// subPixels returns q and the bias and mask that leads to q quantized 133// sub-pixel locations per full pixel. 134// 135// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16, 136// because we want to round fractions of fixed.Int26_6 as: 137// - 0 to 7 rounds to 0. 138// - 8 to 23 rounds to 16. 139// - 24 to 39 rounds to 32. 140// - 40 to 55 rounds to 48. 141// - 56 to 63 rounds to 64. 142// which means to add 8 and then bitwise-and with -16, in two's complement 143// representation. 144// 145// When q == 1, we want bias == 32 and mask == -64. 146// When q == 2, we want bias == 16 and mask == -32. 147// When q == 4, we want bias == 8 and mask == -16. 148// ... 149// When q == 64, we want bias == 0 and mask == -1. (The no-op case). 150// The pattern is clear. 151func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) { 152 return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q) 153} 154 155// glyphCacheEntry caches the arguments and return values of rasterize. 156type glyphCacheEntry struct { 157 key glyphCacheKey 158 val glyphCacheVal 159} 160 161type glyphCacheKey struct { 162 index Index 163 fx, fy uint8 164} 165 166type glyphCacheVal struct { 167 advanceWidth fixed.Int26_6 168 offset image.Point 169 gw int 170 gh int 171} 172 173type indexCacheEntry struct { 174 rune rune 175 index Index 176} 177 178// NewFace returns a new font.Face for the given Font. 179func NewFace(f *Font, opts *Options) font.Face { 180 a := &face{ 181 f: f, 182 hinting: opts.hinting(), 183 scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)), 184 glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()), 185 } 186 a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX() 187 a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY() 188 189 // Fill the cache with invalid entries. Valid glyph cache entries have fx 190 // and fy in the range [0, 64). Valid index cache entries have rune >= 0. 191 for i := range a.glyphCache { 192 a.glyphCache[i].key.fy = 0xff 193 } 194 for i := range a.indexCache { 195 a.indexCache[i].rune = -1 196 } 197 198 // Set the rasterizer's bounds to be big enough to handle the largest glyph. 199 b := f.Bounds(a.scale) 200 xmin := +int(b.Min.X) >> 6 201 ymin := -int(b.Max.Y) >> 6 202 xmax := +int(b.Max.X+63) >> 6 203 ymax := -int(b.Min.Y-63) >> 6 204 a.maxw = xmax - xmin 205 a.maxh = ymax - ymin 206 a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache))) 207 a.r.SetBounds(a.maxw, a.maxh) 208 a.p = facePainter{a} 209 210 return a 211} 212 213type face struct { 214 f *Font 215 hinting font.Hinting 216 scale fixed.Int26_6 217 subPixelX uint32 218 subPixelBiasX fixed.Int26_6 219 subPixelMaskX fixed.Int26_6 220 subPixelY uint32 221 subPixelBiasY fixed.Int26_6 222 subPixelMaskY fixed.Int26_6 223 masks *image.Alpha 224 glyphCache []glyphCacheEntry 225 r raster.Rasterizer 226 p raster.Painter 227 paintOffset int 228 maxw int 229 maxh int 230 glyphBuf GlyphBuf 231 indexCache [indexCacheLen]indexCacheEntry 232 233 // TODO: clip rectangle? 234} 235 236const indexCacheLen = 256 237 238func (a *face) index(r rune) Index { 239 const mask = indexCacheLen - 1 240 c := &a.indexCache[r&mask] 241 if c.rune == r { 242 return c.index 243 } 244 i := a.f.Index(r) 245 c.rune = r 246 c.index = i 247 return i 248} 249 250// Close satisfies the font.Face interface. 251func (a *face) Close() error { return nil } 252 253// Metrics satisfies the font.Face interface. 254func (a *face) Metrics() font.Metrics { 255 scale := float64(a.scale) 256 fupe := float64(a.f.FUnitsPerEm()) 257 return font.Metrics{ 258 Height: a.scale, 259 Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)), 260 Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)), 261 } 262} 263 264// Kern satisfies the font.Face interface. 265func (a *face) Kern(r0, r1 rune) fixed.Int26_6 { 266 i0 := a.index(r0) 267 i1 := a.index(r1) 268 kern := a.f.Kern(a.scale, i0, i1) 269 if a.hinting != font.HintingNone { 270 kern = (kern + 32) &^ 63 271 } 272 return kern 273} 274 275// Glyph satisfies the font.Face interface. 276func (a *face) Glyph(dot fixed.Point26_6, r rune) ( 277 dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { 278 279 // Quantize to the sub-pixel granularity. 280 dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX 281 dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY 282 283 // Split the coordinates into their integer and fractional parts. 284 ix, fx := int(dotX>>6), dotX&0x3f 285 iy, fy := int(dotY>>6), dotY&0x3f 286 287 index := a.index(r) 288 cIndex := uint32(index) 289 cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX) 290 cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY) 291 cIndex &= uint32(len(a.glyphCache) - 1) 292 a.paintOffset = a.maxh * int(cIndex) 293 k := glyphCacheKey{ 294 index: index, 295 fx: uint8(fx), 296 fy: uint8(fy), 297 } 298 var v glyphCacheVal 299 if a.glyphCache[cIndex].key != k { 300 var ok bool 301 v, ok = a.rasterize(index, fx, fy) 302 if !ok { 303 return image.Rectangle{}, nil, image.Point{}, 0, false 304 } 305 a.glyphCache[cIndex] = glyphCacheEntry{k, v} 306 } else { 307 v = a.glyphCache[cIndex].val 308 } 309 310 dr.Min = image.Point{ 311 X: ix + v.offset.X, 312 Y: iy + v.offset.Y, 313 } 314 dr.Max = image.Point{ 315 X: dr.Min.X + v.gw, 316 Y: dr.Min.Y + v.gh, 317 } 318 return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true 319} 320 321func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { 322 if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil { 323 return fixed.Rectangle26_6{}, 0, false 324 } 325 xmin := +a.glyphBuf.Bounds.Min.X 326 ymin := -a.glyphBuf.Bounds.Max.Y 327 xmax := +a.glyphBuf.Bounds.Max.X 328 ymax := -a.glyphBuf.Bounds.Min.Y 329 if xmin > xmax || ymin > ymax { 330 return fixed.Rectangle26_6{}, 0, false 331 } 332 return fixed.Rectangle26_6{ 333 Min: fixed.Point26_6{ 334 X: xmin, 335 Y: ymin, 336 }, 337 Max: fixed.Point26_6{ 338 X: xmax, 339 Y: ymax, 340 }, 341 }, a.glyphBuf.AdvanceWidth, true 342} 343 344func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { 345 if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil { 346 return 0, false 347 } 348 return a.glyphBuf.AdvanceWidth, true 349} 350 351// rasterize returns the advance width, integer-pixel offset to render at, and 352// the width and height of the given glyph at the given sub-pixel offsets. 353// 354// The 26.6 fixed point arguments fx and fy must be in the range [0, 1). 355func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) { 356 if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil { 357 return glyphCacheVal{}, false 358 } 359 // Calculate the integer-pixel bounds for the glyph. 360 xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6 361 ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6 362 xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6 363 ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6 364 if xmin > xmax || ymin > ymax { 365 return glyphCacheVal{}, false 366 } 367 // A TrueType's glyph's nodes can have negative co-ordinates, but the 368 // rasterizer clips anything left of x=0 or above y=0. xmin and ymin are 369 // the pixel offsets, based on the font's FUnit metrics, that let a 370 // negative co-ordinate in TrueType space be non-negative in rasterizer 371 // space. xmin and ymin are typically <= 0. 372 fx -= fixed.Int26_6(xmin << 6) 373 fy -= fixed.Int26_6(ymin << 6) 374 // Rasterize the glyph's vectors. 375 a.r.Clear() 376 pixOffset := a.paintOffset * a.maxw 377 clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh]) 378 e0 := 0 379 for _, e1 := range a.glyphBuf.Ends { 380 a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy) 381 e0 = e1 382 } 383 a.r.Rasterize(a.p) 384 return glyphCacheVal{ 385 a.glyphBuf.AdvanceWidth, 386 image.Point{xmin, ymin}, 387 xmax - xmin, 388 ymax - ymin, 389 }, true 390} 391 392func clear(pix []byte) { 393 for i := range pix { 394 pix[i] = 0 395 } 396} 397 398// drawContour draws the given closed contour with the given offset. 399func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) { 400 if len(ps) == 0 { 401 return 402 } 403 404 // The low bit of each point's Flags value is whether the point is on the 405 // curve. Truetype fonts only have quadratic Bézier curves, not cubics. 406 // Thus, two consecutive off-curve points imply an on-curve point in the 407 // middle of those two. 408 // 409 // See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details. 410 411 // ps[0] is a truetype.Point measured in FUnits and positive Y going 412 // upwards. start is the same thing measured in fixed point units and 413 // positive Y going downwards, and offset by (dx, dy). 414 start := fixed.Point26_6{ 415 X: dx + ps[0].X, 416 Y: dy - ps[0].Y, 417 } 418 var others []Point 419 if ps[0].Flags&0x01 != 0 { 420 others = ps[1:] 421 } else { 422 last := fixed.Point26_6{ 423 X: dx + ps[len(ps)-1].X, 424 Y: dy - ps[len(ps)-1].Y, 425 } 426 if ps[len(ps)-1].Flags&0x01 != 0 { 427 start = last 428 others = ps[:len(ps)-1] 429 } else { 430 start = fixed.Point26_6{ 431 X: (start.X + last.X) / 2, 432 Y: (start.Y + last.Y) / 2, 433 } 434 others = ps 435 } 436 } 437 a.r.Start(start) 438 q0, on0 := start, true 439 for _, p := range others { 440 q := fixed.Point26_6{ 441 X: dx + p.X, 442 Y: dy - p.Y, 443 } 444 on := p.Flags&0x01 != 0 445 if on { 446 if on0 { 447 a.r.Add1(q) 448 } else { 449 a.r.Add2(q0, q) 450 } 451 } else { 452 if on0 { 453 // No-op. 454 } else { 455 mid := fixed.Point26_6{ 456 X: (q0.X + q.X) / 2, 457 Y: (q0.Y + q.Y) / 2, 458 } 459 a.r.Add2(q0, mid) 460 } 461 } 462 q0, on0 = q, on 463 } 464 // Close the curve. 465 if on0 { 466 a.r.Add1(start) 467 } else { 468 a.r.Add2(q0, start) 469 } 470} 471 472// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset 473// (face.paintOffset) to the painted spans. 474type facePainter struct { 475 a *face 476} 477 478func (p facePainter) Paint(ss []raster.Span, done bool) { 479 m := p.a.masks 480 b := m.Bounds() 481 b.Min.Y = p.a.paintOffset 482 b.Max.Y = p.a.paintOffset + p.a.maxh 483 for _, s := range ss { 484 s.Y += p.a.paintOffset 485 if s.Y < b.Min.Y { 486 continue 487 } 488 if s.Y >= b.Max.Y { 489 return 490 } 491 if s.X0 < b.Min.X { 492 s.X0 = b.Min.X 493 } 494 if s.X1 > b.Max.X { 495 s.X1 = b.Max.X 496 } 497 if s.X0 >= s.X1 { 498 continue 499 } 500 base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X 501 p := m.Pix[base+s.X0 : base+s.X1] 502 color := uint8(s.Alpha >> 8) 503 for i := range p { 504 p[i] = color 505 } 506 } 507} 508