github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/freetype/truetype/hint.go (about) 1 // Copyright 2012 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 package truetype 7 8 // This file implements a Truetype bytecode interpreter. 9 // The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html 10 11 import ( 12 "errors" 13 "math" 14 15 "github.com/insionng/yougam/libraries/x/image/math/fixed" 16 ) 17 18 const ( 19 twilightZone = 0 20 glyphZone = 1 21 numZone = 2 22 ) 23 24 type pointType uint32 25 26 const ( 27 current pointType = 0 28 unhinted pointType = 1 29 inFontUnits pointType = 2 30 numPointType = 3 31 ) 32 33 // callStackEntry is a bytecode call stack entry. 34 type callStackEntry struct { 35 program []byte 36 pc int 37 loopCount int32 38 } 39 40 // hinter implements bytecode hinting. A hinter can be re-used to hint a series 41 // of glyphs from a Font. 42 type hinter struct { 43 stack, store []int32 44 45 // functions is a map from function number to bytecode. 46 functions map[int32][]byte 47 48 // font and scale are the font and scale last used for this hinter. 49 // Changing the font will require running the new font's fpgm bytecode. 50 // Changing either will require running the font's prep bytecode. 51 font *Font 52 scale fixed.Int26_6 53 54 // gs and defaultGS are the current and default graphics state. The 55 // default graphics state is the global default graphics state after 56 // the font's fpgm and prep programs have been run. 57 gs, defaultGS graphicsState 58 59 // points and ends are the twilight zone's points, glyph's points 60 // and glyph's contour boundaries. 61 points [numZone][numPointType][]Point 62 ends []int 63 64 // scaledCVT is the lazily initialized scaled Control Value Table. 65 scaledCVTInitialized bool 66 scaledCVT []fixed.Int26_6 67 } 68 69 // graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html 70 type graphicsState struct { 71 // Projection vector, freedom vector and dual projection vector. 72 pv, fv, dv [2]f2dot14 73 // Reference points and zone pointers. 74 rp, zp [3]int32 75 // Control Value / Single Width Cut-In. 76 controlValueCutIn, singleWidthCutIn, singleWidth fixed.Int26_6 77 // Delta base / shift. 78 deltaBase, deltaShift int32 79 // Minimum distance. 80 minDist fixed.Int26_6 81 // Loop count. 82 loop int32 83 // Rounding policy. 84 roundPeriod, roundPhase, roundThreshold fixed.Int26_6 85 roundSuper45 bool 86 // Auto-flip. 87 autoFlip bool 88 } 89 90 var globalDefaultGS = graphicsState{ 91 pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis. 92 fv: [2]f2dot14{0x4000, 0}, 93 dv: [2]f2dot14{0x4000, 0}, 94 zp: [3]int32{1, 1, 1}, 95 controlValueCutIn: (17 << 6) / 16, // 17/16 as a fixed.Int26_6. 96 deltaBase: 9, 97 deltaShift: 3, 98 minDist: 1 << 6, // 1 as a fixed.Int26_6. 99 loop: 1, 100 roundPeriod: 1 << 6, // 1 as a fixed.Int26_6. 101 roundThreshold: 1 << 5, // 1/2 as a fixed.Int26_6. 102 roundSuper45: false, 103 autoFlip: true, 104 } 105 106 func resetTwilightPoints(f *Font, p []Point) []Point { 107 if n := int(f.maxTwilightPoints) + 4; n <= cap(p) { 108 p = p[:n] 109 for i := range p { 110 p[i] = Point{} 111 } 112 } else { 113 p = make([]Point, n) 114 } 115 return p 116 } 117 118 func (h *hinter) init(f *Font, scale fixed.Int26_6) error { 119 h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0]) 120 h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1]) 121 h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2]) 122 123 rescale := h.scale != scale 124 if h.font != f { 125 h.font, rescale = f, true 126 if h.functions == nil { 127 h.functions = make(map[int32][]byte) 128 } else { 129 for k := range h.functions { 130 delete(h.functions, k) 131 } 132 } 133 134 if x := int(f.maxStackElements); x > len(h.stack) { 135 x += 255 136 x &^= 255 137 h.stack = make([]int32, x) 138 } 139 if x := int(f.maxStorage); x > len(h.store) { 140 x += 15 141 x &^= 15 142 h.store = make([]int32, x) 143 } 144 if len(f.fpgm) != 0 { 145 if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil { 146 return err 147 } 148 } 149 } 150 151 if rescale { 152 h.scale = scale 153 h.scaledCVTInitialized = false 154 155 h.defaultGS = globalDefaultGS 156 157 if len(f.prep) != 0 { 158 if err := h.run(f.prep, nil, nil, nil, nil); err != nil { 159 return err 160 } 161 h.defaultGS = h.gs 162 // The MS rasterizer doesn't allow the following graphics state 163 // variables to be modified by the CVT program. 164 h.defaultGS.pv = globalDefaultGS.pv 165 h.defaultGS.fv = globalDefaultGS.fv 166 h.defaultGS.dv = globalDefaultGS.dv 167 h.defaultGS.rp = globalDefaultGS.rp 168 h.defaultGS.zp = globalDefaultGS.zp 169 h.defaultGS.loop = globalDefaultGS.loop 170 } 171 } 172 return nil 173 } 174 175 func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error { 176 h.gs = h.defaultGS 177 h.points[glyphZone][current] = pCurrent 178 h.points[glyphZone][unhinted] = pUnhinted 179 h.points[glyphZone][inFontUnits] = pInFontUnits 180 h.ends = ends 181 182 if len(program) > 50000 { 183 return errors.New("truetype: hinting: too many instructions") 184 } 185 var ( 186 steps, pc, top int 187 opcode uint8 188 189 callStack [32]callStackEntry 190 callStackTop int 191 ) 192 193 for 0 <= pc && pc < len(program) { 194 steps++ 195 if steps == 100000 { 196 return errors.New("truetype: hinting: too many steps") 197 } 198 opcode = program[pc] 199 if top < int(popCount[opcode]) { 200 return errors.New("truetype: hinting: stack underflow") 201 } 202 switch opcode { 203 204 case opSVTCA0: 205 h.gs.pv = [2]f2dot14{0, 0x4000} 206 h.gs.fv = [2]f2dot14{0, 0x4000} 207 h.gs.dv = [2]f2dot14{0, 0x4000} 208 209 case opSVTCA1: 210 h.gs.pv = [2]f2dot14{0x4000, 0} 211 h.gs.fv = [2]f2dot14{0x4000, 0} 212 h.gs.dv = [2]f2dot14{0x4000, 0} 213 214 case opSPVTCA0: 215 h.gs.pv = [2]f2dot14{0, 0x4000} 216 h.gs.dv = [2]f2dot14{0, 0x4000} 217 218 case opSPVTCA1: 219 h.gs.pv = [2]f2dot14{0x4000, 0} 220 h.gs.dv = [2]f2dot14{0x4000, 0} 221 222 case opSFVTCA0: 223 h.gs.fv = [2]f2dot14{0, 0x4000} 224 225 case opSFVTCA1: 226 h.gs.fv = [2]f2dot14{0x4000, 0} 227 228 case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1: 229 top -= 2 230 p1 := h.point(0, current, h.stack[top+0]) 231 p2 := h.point(0, current, h.stack[top+1]) 232 if p1 == nil || p2 == nil { 233 return errors.New("truetype: hinting: point out of range") 234 } 235 dx := f2dot14(p1.X - p2.X) 236 dy := f2dot14(p1.Y - p2.Y) 237 if dx == 0 && dy == 0 { 238 dx = 0x4000 239 } else if opcode&1 != 0 { 240 // Counter-clockwise rotation. 241 dx, dy = -dy, dx 242 } 243 v := normalize(dx, dy) 244 if opcode < opSFVTL0 { 245 h.gs.pv = v 246 h.gs.dv = v 247 } else { 248 h.gs.fv = v 249 } 250 251 case opSPVFS: 252 top -= 2 253 h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1])) 254 h.gs.dv = h.gs.pv 255 256 case opSFVFS: 257 top -= 2 258 h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1])) 259 260 case opGPV: 261 if top+1 >= len(h.stack) { 262 return errors.New("truetype: hinting: stack overflow") 263 } 264 h.stack[top+0] = int32(h.gs.pv[0]) 265 h.stack[top+1] = int32(h.gs.pv[1]) 266 top += 2 267 268 case opGFV: 269 if top+1 >= len(h.stack) { 270 return errors.New("truetype: hinting: stack overflow") 271 } 272 h.stack[top+0] = int32(h.gs.fv[0]) 273 h.stack[top+1] = int32(h.gs.fv[1]) 274 top += 2 275 276 case opSFVTPV: 277 h.gs.fv = h.gs.pv 278 279 case opISECT: 280 top -= 5 281 p := h.point(2, current, h.stack[top+0]) 282 a0 := h.point(1, current, h.stack[top+1]) 283 a1 := h.point(1, current, h.stack[top+2]) 284 b0 := h.point(0, current, h.stack[top+3]) 285 b1 := h.point(0, current, h.stack[top+4]) 286 if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil { 287 return errors.New("truetype: hinting: point out of range") 288 } 289 290 dbx := b1.X - b0.X 291 dby := b1.Y - b0.Y 292 dax := a1.X - a0.X 293 day := a1.Y - a0.Y 294 dx := b0.X - a0.X 295 dy := b0.Y - a0.Y 296 discriminant := mulDiv(int64(dax), int64(-dby), 0x40) + 297 mulDiv(int64(day), int64(dbx), 0x40) 298 dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) + 299 mulDiv(int64(day), int64(dby), 0x40) 300 // The discriminant above is actually a cross product of vectors 301 // da and db. Together with the dot product, they can be used as 302 // surrogates for sine and cosine of the angle between the vectors. 303 // Indeed, 304 // dotproduct = |da||db|cos(angle) 305 // discriminant = |da||db|sin(angle) 306 // We use these equations to reject grazing intersections by 307 // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. 308 absDisc, absDotP := discriminant, dotProduct 309 if absDisc < 0 { 310 absDisc = -absDisc 311 } 312 if absDotP < 0 { 313 absDotP = -absDotP 314 } 315 if 19*absDisc > absDotP { 316 val := mulDiv(int64(dx), int64(-dby), 0x40) + 317 mulDiv(int64(dy), int64(dbx), 0x40) 318 rx := mulDiv(val, int64(dax), discriminant) 319 ry := mulDiv(val, int64(day), discriminant) 320 p.X = a0.X + fixed.Int26_6(rx) 321 p.Y = a0.Y + fixed.Int26_6(ry) 322 } else { 323 p.X = (a0.X + a1.X + b0.X + b1.X) / 4 324 p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4 325 } 326 p.Flags |= flagTouchedX | flagTouchedY 327 328 case opSRP0, opSRP1, opSRP2: 329 top-- 330 h.gs.rp[opcode-opSRP0] = h.stack[top] 331 332 case opSZP0, opSZP1, opSZP2: 333 top-- 334 h.gs.zp[opcode-opSZP0] = h.stack[top] 335 336 case opSZPS: 337 top-- 338 h.gs.zp[0] = h.stack[top] 339 h.gs.zp[1] = h.stack[top] 340 h.gs.zp[2] = h.stack[top] 341 342 case opSLOOP: 343 top-- 344 if h.stack[top] <= 0 { 345 return errors.New("truetype: hinting: invalid data") 346 } 347 h.gs.loop = h.stack[top] 348 349 case opRTG: 350 h.gs.roundPeriod = 1 << 6 351 h.gs.roundPhase = 0 352 h.gs.roundThreshold = 1 << 5 353 h.gs.roundSuper45 = false 354 355 case opRTHG: 356 h.gs.roundPeriod = 1 << 6 357 h.gs.roundPhase = 1 << 5 358 h.gs.roundThreshold = 1 << 5 359 h.gs.roundSuper45 = false 360 361 case opSMD: 362 top-- 363 h.gs.minDist = fixed.Int26_6(h.stack[top]) 364 365 case opELSE: 366 opcode = 1 367 goto ifelse 368 369 case opJMPR: 370 top-- 371 pc += int(h.stack[top]) 372 continue 373 374 case opSCVTCI: 375 top-- 376 h.gs.controlValueCutIn = fixed.Int26_6(h.stack[top]) 377 378 case opSSWCI: 379 top-- 380 h.gs.singleWidthCutIn = fixed.Int26_6(h.stack[top]) 381 382 case opSSW: 383 top-- 384 h.gs.singleWidth = h.font.scale(h.scale * fixed.Int26_6(h.stack[top])) 385 386 case opDUP: 387 if top >= len(h.stack) { 388 return errors.New("truetype: hinting: stack overflow") 389 } 390 h.stack[top] = h.stack[top-1] 391 top++ 392 393 case opPOP: 394 top-- 395 396 case opCLEAR: 397 top = 0 398 399 case opSWAP: 400 h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1] 401 402 case opDEPTH: 403 if top >= len(h.stack) { 404 return errors.New("truetype: hinting: stack overflow") 405 } 406 h.stack[top] = int32(top) 407 top++ 408 409 case opCINDEX, opMINDEX: 410 x := int(h.stack[top-1]) 411 if x <= 0 || x >= top { 412 return errors.New("truetype: hinting: invalid data") 413 } 414 h.stack[top-1] = h.stack[top-1-x] 415 if opcode == opMINDEX { 416 copy(h.stack[top-1-x:top-1], h.stack[top-x:top]) 417 top-- 418 } 419 420 case opALIGNPTS: 421 top -= 2 422 p := h.point(1, current, h.stack[top]) 423 q := h.point(0, current, h.stack[top+1]) 424 if p == nil || q == nil { 425 return errors.New("truetype: hinting: point out of range") 426 } 427 d := dotProduct(fixed.Int26_6(q.X-p.X), fixed.Int26_6(q.Y-p.Y), h.gs.pv) / 2 428 h.move(p, +d, true) 429 h.move(q, -d, true) 430 431 case opUTP: 432 top-- 433 p := h.point(0, current, h.stack[top]) 434 if p == nil { 435 return errors.New("truetype: hinting: point out of range") 436 } 437 p.Flags &^= flagTouchedX | flagTouchedY 438 439 case opLOOPCALL, opCALL: 440 if callStackTop >= len(callStack) { 441 return errors.New("truetype: hinting: call stack overflow") 442 } 443 top-- 444 f, ok := h.functions[h.stack[top]] 445 if !ok { 446 return errors.New("truetype: hinting: undefined function") 447 } 448 callStack[callStackTop] = callStackEntry{program, pc, 1} 449 if opcode == opLOOPCALL { 450 top-- 451 if h.stack[top] == 0 { 452 break 453 } 454 callStack[callStackTop].loopCount = h.stack[top] 455 } 456 callStackTop++ 457 program, pc = f, 0 458 continue 459 460 case opFDEF: 461 // Save all bytecode up until the next ENDF. 462 startPC := pc + 1 463 fdefloop: 464 for { 465 pc++ 466 if pc >= len(program) { 467 return errors.New("truetype: hinting: unbalanced FDEF") 468 } 469 switch program[pc] { 470 case opFDEF: 471 return errors.New("truetype: hinting: nested FDEF") 472 case opENDF: 473 top-- 474 h.functions[h.stack[top]] = program[startPC : pc+1] 475 break fdefloop 476 default: 477 var ok bool 478 pc, ok = skipInstructionPayload(program, pc) 479 if !ok { 480 return errors.New("truetype: hinting: unbalanced FDEF") 481 } 482 } 483 } 484 485 case opENDF: 486 if callStackTop == 0 { 487 return errors.New("truetype: hinting: call stack underflow") 488 } 489 callStackTop-- 490 callStack[callStackTop].loopCount-- 491 if callStack[callStackTop].loopCount != 0 { 492 callStackTop++ 493 pc = 0 494 continue 495 } 496 program, pc = callStack[callStackTop].program, callStack[callStackTop].pc 497 498 case opMDAP0, opMDAP1: 499 top-- 500 i := h.stack[top] 501 p := h.point(0, current, i) 502 if p == nil { 503 return errors.New("truetype: hinting: point out of range") 504 } 505 distance := fixed.Int26_6(0) 506 if opcode == opMDAP1 { 507 distance = dotProduct(p.X, p.Y, h.gs.pv) 508 // TODO: metrics compensation. 509 distance = h.round(distance) - distance 510 } 511 h.move(p, distance, true) 512 h.gs.rp[0] = i 513 h.gs.rp[1] = i 514 515 case opIUP0, opIUP1: 516 iupY, mask := opcode == opIUP0, uint32(flagTouchedX) 517 if iupY { 518 mask = flagTouchedY 519 } 520 prevEnd := 0 521 for _, end := range h.ends { 522 for i := prevEnd; i < end; i++ { 523 for i < end && h.points[glyphZone][current][i].Flags&mask == 0 { 524 i++ 525 } 526 if i == end { 527 break 528 } 529 firstTouched, curTouched := i, i 530 i++ 531 for ; i < end; i++ { 532 if h.points[glyphZone][current][i].Flags&mask != 0 { 533 h.iupInterp(iupY, curTouched+1, i-1, curTouched, i) 534 curTouched = i 535 } 536 } 537 if curTouched == firstTouched { 538 h.iupShift(iupY, prevEnd, end, curTouched) 539 } else { 540 h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched) 541 if firstTouched > 0 { 542 h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched) 543 } 544 } 545 } 546 prevEnd = end 547 } 548 549 case opSHP0, opSHP1: 550 if top < int(h.gs.loop) { 551 return errors.New("truetype: hinting: stack underflow") 552 } 553 _, _, d, ok := h.displacement(opcode&1 == 0) 554 if !ok { 555 return errors.New("truetype: hinting: point out of range") 556 } 557 for ; h.gs.loop != 0; h.gs.loop-- { 558 top-- 559 p := h.point(2, current, h.stack[top]) 560 if p == nil { 561 return errors.New("truetype: hinting: point out of range") 562 } 563 h.move(p, d, true) 564 } 565 h.gs.loop = 1 566 567 case opSHC0, opSHC1: 568 top-- 569 zonePointer, i, d, ok := h.displacement(opcode&1 == 0) 570 if !ok { 571 return errors.New("truetype: hinting: point out of range") 572 } 573 if h.gs.zp[2] == 0 { 574 // TODO: implement this when we have a glyph that does this. 575 return errors.New("hinting: unimplemented SHC instruction") 576 } 577 contour := h.stack[top] 578 if contour < 0 || len(ends) <= int(contour) { 579 return errors.New("truetype: hinting: contour out of range") 580 } 581 j0, j1 := int32(0), int32(h.ends[contour]) 582 if contour > 0 { 583 j0 = int32(h.ends[contour-1]) 584 } 585 move := h.gs.zp[zonePointer] != h.gs.zp[2] 586 for j := j0; j < j1; j++ { 587 if move || j != i { 588 h.move(h.point(2, current, j), d, true) 589 } 590 } 591 592 case opSHZ0, opSHZ1: 593 top-- 594 zonePointer, i, d, ok := h.displacement(opcode&1 == 0) 595 if !ok { 596 return errors.New("truetype: hinting: point out of range") 597 } 598 599 // As per C Freetype, SHZ doesn't move the phantom points, or mark 600 // the points as touched. 601 limit := int32(len(h.points[h.gs.zp[2]][current])) 602 if h.gs.zp[2] == glyphZone { 603 limit -= 4 604 } 605 for j := int32(0); j < limit; j++ { 606 if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] { 607 h.move(h.point(2, current, j), d, false) 608 } 609 } 610 611 case opSHPIX: 612 top-- 613 d := fixed.Int26_6(h.stack[top]) 614 if top < int(h.gs.loop) { 615 return errors.New("truetype: hinting: stack underflow") 616 } 617 for ; h.gs.loop != 0; h.gs.loop-- { 618 top-- 619 p := h.point(2, current, h.stack[top]) 620 if p == nil { 621 return errors.New("truetype: hinting: point out of range") 622 } 623 h.move(p, d, true) 624 } 625 h.gs.loop = 1 626 627 case opIP: 628 if top < int(h.gs.loop) { 629 return errors.New("truetype: hinting: stack underflow") 630 } 631 pointType := inFontUnits 632 twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0 633 if twilight { 634 pointType = unhinted 635 } 636 p := h.point(1, pointType, h.gs.rp[2]) 637 oldP := h.point(0, pointType, h.gs.rp[1]) 638 oldRange := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv) 639 640 p = h.point(1, current, h.gs.rp[2]) 641 curP := h.point(0, current, h.gs.rp[1]) 642 curRange := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv) 643 for ; h.gs.loop != 0; h.gs.loop-- { 644 top-- 645 i := h.stack[top] 646 p = h.point(2, pointType, i) 647 oldDist := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv) 648 p = h.point(2, current, i) 649 curDist := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv) 650 newDist := fixed.Int26_6(0) 651 if oldDist != 0 { 652 if oldRange != 0 { 653 newDist = fixed.Int26_6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange))) 654 } else { 655 newDist = -oldDist 656 } 657 } 658 h.move(p, newDist-curDist, true) 659 } 660 h.gs.loop = 1 661 662 case opMSIRP0, opMSIRP1: 663 top -= 2 664 i := h.stack[top] 665 distance := fixed.Int26_6(h.stack[top+1]) 666 667 // TODO: special case h.gs.zp[1] == 0 in C Freetype. 668 ref := h.point(0, current, h.gs.rp[0]) 669 p := h.point(1, current, i) 670 if ref == nil || p == nil { 671 return errors.New("truetype: hinting: point out of range") 672 } 673 curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv) 674 675 // Set-RP0 bit. 676 if opcode == opMSIRP1 { 677 h.gs.rp[0] = i 678 } 679 h.gs.rp[1] = h.gs.rp[0] 680 h.gs.rp[2] = i 681 682 // Move the point. 683 h.move(p, distance-curDist, true) 684 685 case opALIGNRP: 686 if top < int(h.gs.loop) { 687 return errors.New("truetype: hinting: stack underflow") 688 } 689 ref := h.point(0, current, h.gs.rp[0]) 690 if ref == nil { 691 return errors.New("truetype: hinting: point out of range") 692 } 693 for ; h.gs.loop != 0; h.gs.loop-- { 694 top-- 695 p := h.point(1, current, h.stack[top]) 696 if p == nil { 697 return errors.New("truetype: hinting: point out of range") 698 } 699 h.move(p, -dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv), true) 700 } 701 h.gs.loop = 1 702 703 case opRTDG: 704 h.gs.roundPeriod = 1 << 5 705 h.gs.roundPhase = 0 706 h.gs.roundThreshold = 1 << 4 707 h.gs.roundSuper45 = false 708 709 case opMIAP0, opMIAP1: 710 top -= 2 711 i := h.stack[top] 712 distance := h.getScaledCVT(h.stack[top+1]) 713 if h.gs.zp[0] == 0 { 714 p := h.point(0, unhinted, i) 715 q := h.point(0, current, i) 716 p.X = fixed.Int26_6((int64(distance) * int64(h.gs.fv[0])) >> 14) 717 p.Y = fixed.Int26_6((int64(distance) * int64(h.gs.fv[1])) >> 14) 718 *q = *p 719 } 720 p := h.point(0, current, i) 721 oldDist := dotProduct(p.X, p.Y, h.gs.pv) 722 if opcode == opMIAP1 { 723 if fabs(distance-oldDist) > h.gs.controlValueCutIn { 724 distance = oldDist 725 } 726 // TODO: metrics compensation. 727 distance = h.round(distance) 728 } 729 h.move(p, distance-oldDist, true) 730 h.gs.rp[0] = i 731 h.gs.rp[1] = i 732 733 case opNPUSHB: 734 opcode = 0 735 goto push 736 737 case opNPUSHW: 738 opcode = 0x80 739 goto push 740 741 case opWS: 742 top -= 2 743 i := int(h.stack[top]) 744 if i < 0 || len(h.store) <= i { 745 return errors.New("truetype: hinting: invalid data") 746 } 747 h.store[i] = h.stack[top+1] 748 749 case opRS: 750 i := int(h.stack[top-1]) 751 if i < 0 || len(h.store) <= i { 752 return errors.New("truetype: hinting: invalid data") 753 } 754 h.stack[top-1] = h.store[i] 755 756 case opWCVTP: 757 top -= 2 758 h.setScaledCVT(h.stack[top], fixed.Int26_6(h.stack[top+1])) 759 760 case opRCVT: 761 h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1])) 762 763 case opGC0, opGC1: 764 i := h.stack[top-1] 765 if opcode == opGC0 { 766 p := h.point(2, current, i) 767 h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.pv)) 768 } else { 769 p := h.point(2, unhinted, i) 770 // Using dv as per C Freetype. 771 h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.dv)) 772 } 773 774 case opSCFS: 775 top -= 2 776 i := h.stack[top] 777 p := h.point(2, current, i) 778 if p == nil { 779 return errors.New("truetype: hinting: point out of range") 780 } 781 c := dotProduct(p.X, p.Y, h.gs.pv) 782 h.move(p, fixed.Int26_6(h.stack[top+1])-c, true) 783 if h.gs.zp[2] != 0 { 784 break 785 } 786 q := h.point(2, unhinted, i) 787 if q == nil { 788 return errors.New("truetype: hinting: point out of range") 789 } 790 q.X = p.X 791 q.Y = p.Y 792 793 case opMD0, opMD1: 794 top-- 795 pt, v, scale := pointType(0), [2]f2dot14{}, false 796 if opcode == opMD0 { 797 pt = current 798 v = h.gs.pv 799 } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 { 800 pt = unhinted 801 v = h.gs.dv 802 } else { 803 pt = inFontUnits 804 v = h.gs.dv 805 scale = true 806 } 807 p := h.point(0, pt, h.stack[top-1]) 808 q := h.point(1, pt, h.stack[top]) 809 if p == nil || q == nil { 810 return errors.New("truetype: hinting: point out of range") 811 } 812 d := int32(dotProduct(p.X-q.X, p.Y-q.Y, v)) 813 if scale { 814 d = int32(int64(d*int32(h.scale)) / int64(h.font.fUnitsPerEm)) 815 } 816 h.stack[top-1] = d 817 818 case opMPPEM, opMPS: 819 if top >= len(h.stack) { 820 return errors.New("truetype: hinting: stack overflow") 821 } 822 // For MPS, point size should be irrelevant; we return the PPEM. 823 h.stack[top] = int32(h.scale) >> 6 824 top++ 825 826 case opFLIPON, opFLIPOFF: 827 h.gs.autoFlip = opcode == opFLIPON 828 829 case opDEBUG: 830 // No-op. 831 832 case opLT: 833 top-- 834 h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top]) 835 836 case opLTEQ: 837 top-- 838 h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top]) 839 840 case opGT: 841 top-- 842 h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top]) 843 844 case opGTEQ: 845 top-- 846 h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top]) 847 848 case opEQ: 849 top-- 850 h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top]) 851 852 case opNEQ: 853 top-- 854 h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top]) 855 856 case opODD, opEVEN: 857 i := h.round(fixed.Int26_6(h.stack[top-1])) >> 6 858 h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD) 859 860 case opIF: 861 top-- 862 if h.stack[top] == 0 { 863 opcode = 0 864 goto ifelse 865 } 866 867 case opEIF: 868 // No-op. 869 870 case opAND: 871 top-- 872 h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0) 873 874 case opOR: 875 top-- 876 h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0) 877 878 case opNOT: 879 h.stack[top-1] = bool2int32(h.stack[top-1] == 0) 880 881 case opDELTAP1: 882 goto delta 883 884 case opSDB: 885 top-- 886 h.gs.deltaBase = h.stack[top] 887 888 case opSDS: 889 top-- 890 h.gs.deltaShift = h.stack[top] 891 892 case opADD: 893 top-- 894 h.stack[top-1] += h.stack[top] 895 896 case opSUB: 897 top-- 898 h.stack[top-1] -= h.stack[top] 899 900 case opDIV: 901 top-- 902 if h.stack[top] == 0 { 903 return errors.New("truetype: hinting: division by zero") 904 } 905 h.stack[top-1] = int32(fdiv(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top]))) 906 907 case opMUL: 908 top-- 909 h.stack[top-1] = int32(fmul(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top]))) 910 911 case opABS: 912 if h.stack[top-1] < 0 { 913 h.stack[top-1] = -h.stack[top-1] 914 } 915 916 case opNEG: 917 h.stack[top-1] = -h.stack[top-1] 918 919 case opFLOOR: 920 h.stack[top-1] &^= 63 921 922 case opCEILING: 923 h.stack[top-1] += 63 924 h.stack[top-1] &^= 63 925 926 case opROUND00, opROUND01, opROUND10, opROUND11: 927 // The four flavors of opROUND are equivalent. See the comment below on 928 // opNROUND for the rationale. 929 h.stack[top-1] = int32(h.round(fixed.Int26_6(h.stack[top-1]))) 930 931 case opNROUND00, opNROUND01, opNROUND10, opNROUND11: 932 // No-op. The spec says to add one of four "compensations for the engine 933 // characteristics", to cater for things like "different dot-size printers". 934 // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation 935 // This code does not implement engine compensation, as we don't expect to 936 // be used to output on dot-matrix printers. 937 938 case opWCVTF: 939 top -= 2 940 h.setScaledCVT(h.stack[top], h.font.scale(h.scale*fixed.Int26_6(h.stack[top+1]))) 941 942 case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3: 943 goto delta 944 945 case opSROUND, opS45ROUND: 946 top-- 947 switch (h.stack[top] >> 6) & 0x03 { 948 case 0: 949 h.gs.roundPeriod = 1 << 5 950 case 1, 3: 951 h.gs.roundPeriod = 1 << 6 952 case 2: 953 h.gs.roundPeriod = 1 << 7 954 } 955 h.gs.roundSuper45 = opcode == opS45ROUND 956 if h.gs.roundSuper45 { 957 // The spec says to multiply by √2, but the C Freetype code says 1/√2. 958 // We go with 1/√2. 959 h.gs.roundPeriod *= 46341 960 h.gs.roundPeriod /= 65536 961 } 962 h.gs.roundPhase = h.gs.roundPeriod * fixed.Int26_6((h.stack[top]>>4)&0x03) / 4 963 if x := h.stack[top] & 0x0f; x != 0 { 964 h.gs.roundThreshold = h.gs.roundPeriod * fixed.Int26_6(x-4) / 8 965 } else { 966 h.gs.roundThreshold = h.gs.roundPeriod - 1 967 } 968 969 case opJROT: 970 top -= 2 971 if h.stack[top+1] != 0 { 972 pc += int(h.stack[top]) 973 continue 974 } 975 976 case opJROF: 977 top -= 2 978 if h.stack[top+1] == 0 { 979 pc += int(h.stack[top]) 980 continue 981 } 982 983 case opROFF: 984 h.gs.roundPeriod = 0 985 h.gs.roundPhase = 0 986 h.gs.roundThreshold = 0 987 h.gs.roundSuper45 = false 988 989 case opRUTG: 990 h.gs.roundPeriod = 1 << 6 991 h.gs.roundPhase = 0 992 h.gs.roundThreshold = 1<<6 - 1 993 h.gs.roundSuper45 = false 994 995 case opRDTG: 996 h.gs.roundPeriod = 1 << 6 997 h.gs.roundPhase = 0 998 h.gs.roundThreshold = 0 999 h.gs.roundSuper45 = false 1000 1001 case opSANGW, opAA: 1002 // These ops are "anachronistic" and no longer used. 1003 top-- 1004 1005 case opFLIPPT: 1006 if top < int(h.gs.loop) { 1007 return errors.New("truetype: hinting: stack underflow") 1008 } 1009 points := h.points[glyphZone][current] 1010 for ; h.gs.loop != 0; h.gs.loop-- { 1011 top-- 1012 i := h.stack[top] 1013 if i < 0 || len(points) <= int(i) { 1014 return errors.New("truetype: hinting: point out of range") 1015 } 1016 points[i].Flags ^= flagOnCurve 1017 } 1018 h.gs.loop = 1 1019 1020 case opFLIPRGON, opFLIPRGOFF: 1021 top -= 2 1022 i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current] 1023 if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) { 1024 return errors.New("truetype: hinting: point out of range") 1025 } 1026 for ; i <= j; i++ { 1027 if opcode == opFLIPRGON { 1028 points[i].Flags |= flagOnCurve 1029 } else { 1030 points[i].Flags &^= flagOnCurve 1031 } 1032 } 1033 1034 case opSCANCTRL: 1035 // We do not support dropout control, as we always rasterize grayscale glyphs. 1036 top-- 1037 1038 case opSDPVTL0, opSDPVTL1: 1039 top -= 2 1040 for i := 0; i < 2; i++ { 1041 pt := unhinted 1042 if i != 0 { 1043 pt = current 1044 } 1045 p := h.point(1, pt, h.stack[top]) 1046 q := h.point(2, pt, h.stack[top+1]) 1047 if p == nil || q == nil { 1048 return errors.New("truetype: hinting: point out of range") 1049 } 1050 dx := f2dot14(p.X - q.X) 1051 dy := f2dot14(p.Y - q.Y) 1052 if dx == 0 && dy == 0 { 1053 dx = 0x4000 1054 } else if opcode&1 != 0 { 1055 // Counter-clockwise rotation. 1056 dx, dy = -dy, dx 1057 } 1058 if i == 0 { 1059 h.gs.dv = normalize(dx, dy) 1060 } else { 1061 h.gs.pv = normalize(dx, dy) 1062 } 1063 } 1064 1065 case opGETINFO: 1066 res := int32(0) 1067 if h.stack[top-1]&(1<<0) != 0 { 1068 // Set the engine version. We hard-code this to 35, the same as 1069 // the C freetype code, which says that "Version~35 corresponds 1070 // to MS rasterizer v.1.7 as used e.g. in Windows~98". 1071 res |= 35 1072 } 1073 if h.stack[top-1]&(1<<5) != 0 { 1074 // Set that we support grayscale. 1075 res |= 1 << 12 1076 } 1077 // We set no other bits, as we do not support rotated or stretched glyphs. 1078 h.stack[top-1] = res 1079 1080 case opIDEF: 1081 // IDEF is for ancient versions of the bytecode interpreter, and is no longer used. 1082 return errors.New("truetype: hinting: unsupported IDEF instruction") 1083 1084 case opROLL: 1085 h.stack[top-1], h.stack[top-3], h.stack[top-2] = 1086 h.stack[top-3], h.stack[top-2], h.stack[top-1] 1087 1088 case opMAX: 1089 top-- 1090 if h.stack[top-1] < h.stack[top] { 1091 h.stack[top-1] = h.stack[top] 1092 } 1093 1094 case opMIN: 1095 top-- 1096 if h.stack[top-1] > h.stack[top] { 1097 h.stack[top-1] = h.stack[top] 1098 } 1099 1100 case opSCANTYPE: 1101 // We do not support dropout control, as we always rasterize grayscale glyphs. 1102 top-- 1103 1104 case opINSTCTRL: 1105 // TODO: support instruction execution control? It seems rare, and even when 1106 // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or 1107 // unusual rasterization conditions. For example, the code snippet at 1108 // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL 1109 // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but 1110 // freetype-go does not support rotated or stretched glyphs. 1111 top -= 2 1112 1113 default: 1114 if opcode < opPUSHB000 { 1115 return errors.New("truetype: hinting: unrecognized instruction") 1116 } 1117 1118 if opcode < opMDRP00000 { 1119 // PUSHxxxx opcode. 1120 1121 if opcode < opPUSHW000 { 1122 opcode -= opPUSHB000 - 1 1123 } else { 1124 opcode -= opPUSHW000 - 1 - 0x80 1125 } 1126 goto push 1127 } 1128 1129 if opcode < opMIRP00000 { 1130 // MDRPxxxxx opcode. 1131 1132 top-- 1133 i := h.stack[top] 1134 ref := h.point(0, current, h.gs.rp[0]) 1135 p := h.point(1, current, i) 1136 if ref == nil || p == nil { 1137 return errors.New("truetype: hinting: point out of range") 1138 } 1139 1140 oldDist := fixed.Int26_6(0) 1141 if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 { 1142 p0 := h.point(1, unhinted, i) 1143 p1 := h.point(0, unhinted, h.gs.rp[0]) 1144 oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv) 1145 } else { 1146 p0 := h.point(1, inFontUnits, i) 1147 p1 := h.point(0, inFontUnits, h.gs.rp[0]) 1148 oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv) 1149 oldDist = h.font.scale(h.scale * oldDist) 1150 } 1151 1152 // Single-width cut-in test. 1153 if x := fabs(oldDist - h.gs.singleWidth); x < h.gs.singleWidthCutIn { 1154 if oldDist >= 0 { 1155 oldDist = +h.gs.singleWidth 1156 } else { 1157 oldDist = -h.gs.singleWidth 1158 } 1159 } 1160 1161 // Rounding bit. 1162 // TODO: metrics compensation. 1163 distance := oldDist 1164 if opcode&0x04 != 0 { 1165 distance = h.round(oldDist) 1166 } 1167 1168 // Minimum distance bit. 1169 if opcode&0x08 != 0 { 1170 if oldDist >= 0 { 1171 if distance < h.gs.minDist { 1172 distance = h.gs.minDist 1173 } 1174 } else { 1175 if distance > -h.gs.minDist { 1176 distance = -h.gs.minDist 1177 } 1178 } 1179 } 1180 1181 // Set-RP0 bit. 1182 h.gs.rp[1] = h.gs.rp[0] 1183 h.gs.rp[2] = i 1184 if opcode&0x10 != 0 { 1185 h.gs.rp[0] = i 1186 } 1187 1188 // Move the point. 1189 oldDist = dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv) 1190 h.move(p, distance-oldDist, true) 1191 1192 } else { 1193 // MIRPxxxxx opcode. 1194 1195 top -= 2 1196 i := h.stack[top] 1197 cvtDist := h.getScaledCVT(h.stack[top+1]) 1198 if fabs(cvtDist-h.gs.singleWidth) < h.gs.singleWidthCutIn { 1199 if cvtDist >= 0 { 1200 cvtDist = +h.gs.singleWidth 1201 } else { 1202 cvtDist = -h.gs.singleWidth 1203 } 1204 } 1205 1206 if h.gs.zp[1] == 0 { 1207 // TODO: implement once we have a .ttf file that triggers 1208 // this, so that we can step through C's freetype. 1209 return errors.New("truetype: hinting: unimplemented twilight point adjustment") 1210 } 1211 1212 ref := h.point(0, unhinted, h.gs.rp[0]) 1213 p := h.point(1, unhinted, i) 1214 if ref == nil || p == nil { 1215 return errors.New("truetype: hinting: point out of range") 1216 } 1217 oldDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.dv) 1218 1219 ref = h.point(0, current, h.gs.rp[0]) 1220 p = h.point(1, current, i) 1221 if ref == nil || p == nil { 1222 return errors.New("truetype: hinting: point out of range") 1223 } 1224 curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv) 1225 1226 if h.gs.autoFlip && oldDist^cvtDist < 0 { 1227 cvtDist = -cvtDist 1228 } 1229 1230 // Rounding bit. 1231 // TODO: metrics compensation. 1232 distance := cvtDist 1233 if opcode&0x04 != 0 { 1234 // The CVT value is only used if close enough to oldDist. 1235 if (h.gs.zp[0] == h.gs.zp[1]) && 1236 (fabs(cvtDist-oldDist) > h.gs.controlValueCutIn) { 1237 1238 distance = oldDist 1239 } 1240 distance = h.round(distance) 1241 } 1242 1243 // Minimum distance bit. 1244 if opcode&0x08 != 0 { 1245 if oldDist >= 0 { 1246 if distance < h.gs.minDist { 1247 distance = h.gs.minDist 1248 } 1249 } else { 1250 if distance > -h.gs.minDist { 1251 distance = -h.gs.minDist 1252 } 1253 } 1254 } 1255 1256 // Set-RP0 bit. 1257 h.gs.rp[1] = h.gs.rp[0] 1258 h.gs.rp[2] = i 1259 if opcode&0x10 != 0 { 1260 h.gs.rp[0] = i 1261 } 1262 1263 // Move the point. 1264 h.move(p, distance-curDist, true) 1265 } 1266 } 1267 pc++ 1268 continue 1269 1270 ifelse: 1271 // Skip past bytecode until the next ELSE (if opcode == 0) or the 1272 // next EIF (for all opcodes). Opcode == 0 means that we have come 1273 // from an IF. Opcode == 1 means that we have come from an ELSE. 1274 { 1275 ifelseloop: 1276 for depth := 0; ; { 1277 pc++ 1278 if pc >= len(program) { 1279 return errors.New("truetype: hinting: unbalanced IF or ELSE") 1280 } 1281 switch program[pc] { 1282 case opIF: 1283 depth++ 1284 case opELSE: 1285 if depth == 0 && opcode == 0 { 1286 break ifelseloop 1287 } 1288 case opEIF: 1289 depth-- 1290 if depth < 0 { 1291 break ifelseloop 1292 } 1293 default: 1294 var ok bool 1295 pc, ok = skipInstructionPayload(program, pc) 1296 if !ok { 1297 return errors.New("truetype: hinting: unbalanced IF or ELSE") 1298 } 1299 } 1300 } 1301 pc++ 1302 continue 1303 } 1304 1305 push: 1306 // Push n elements from the program to the stack, where n is the low 7 bits of 1307 // opcode. If the low 7 bits are zero, then n is the next byte from the program. 1308 // The high bit being 0 means that the elements are zero-extended bytes. 1309 // The high bit being 1 means that the elements are sign-extended words. 1310 { 1311 width := 1 1312 if opcode&0x80 != 0 { 1313 opcode &^= 0x80 1314 width = 2 1315 } 1316 if opcode == 0 { 1317 pc++ 1318 if pc >= len(program) { 1319 return errors.New("truetype: hinting: insufficient data") 1320 } 1321 opcode = program[pc] 1322 } 1323 pc++ 1324 if top+int(opcode) > len(h.stack) { 1325 return errors.New("truetype: hinting: stack overflow") 1326 } 1327 if pc+width*int(opcode) > len(program) { 1328 return errors.New("truetype: hinting: insufficient data") 1329 } 1330 for ; opcode > 0; opcode-- { 1331 if width == 1 { 1332 h.stack[top] = int32(program[pc]) 1333 } else { 1334 h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1]) 1335 } 1336 top++ 1337 pc += width 1338 } 1339 continue 1340 } 1341 1342 delta: 1343 { 1344 if opcode >= opDELTAC1 && !h.scaledCVTInitialized { 1345 h.initializeScaledCVT() 1346 } 1347 top-- 1348 n := h.stack[top] 1349 if int32(top) < 2*n { 1350 return errors.New("truetype: hinting: stack underflow") 1351 } 1352 for ; n > 0; n-- { 1353 top -= 2 1354 b := h.stack[top] 1355 c := (b & 0xf0) >> 4 1356 switch opcode { 1357 case opDELTAP2, opDELTAC2: 1358 c += 16 1359 case opDELTAP3, opDELTAC3: 1360 c += 32 1361 } 1362 c += h.gs.deltaBase 1363 if ppem := (int32(h.scale) + 1<<5) >> 6; ppem != c { 1364 continue 1365 } 1366 b = (b & 0x0f) - 8 1367 if b >= 0 { 1368 b++ 1369 } 1370 b = b * 64 / (1 << uint32(h.gs.deltaShift)) 1371 if opcode >= opDELTAC1 { 1372 a := h.stack[top+1] 1373 if a < 0 || len(h.scaledCVT) <= int(a) { 1374 return errors.New("truetype: hinting: index out of range") 1375 } 1376 h.scaledCVT[a] += fixed.Int26_6(b) 1377 } else { 1378 p := h.point(0, current, h.stack[top+1]) 1379 if p == nil { 1380 return errors.New("truetype: hinting: point out of range") 1381 } 1382 h.move(p, fixed.Int26_6(b), true) 1383 } 1384 } 1385 pc++ 1386 continue 1387 } 1388 } 1389 return nil 1390 } 1391 1392 func (h *hinter) initializeScaledCVT() { 1393 h.scaledCVTInitialized = true 1394 if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) { 1395 h.scaledCVT = h.scaledCVT[:n] 1396 } else { 1397 if n < 32 { 1398 n = 32 1399 } 1400 h.scaledCVT = make([]fixed.Int26_6, len(h.font.cvt)/2, n) 1401 } 1402 for i := range h.scaledCVT { 1403 unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1]) 1404 h.scaledCVT[i] = h.font.scale(h.scale * fixed.Int26_6(int16(unscaled))) 1405 } 1406 } 1407 1408 // getScaledCVT returns the scaled value from the font's Control Value Table. 1409 func (h *hinter) getScaledCVT(i int32) fixed.Int26_6 { 1410 if !h.scaledCVTInitialized { 1411 h.initializeScaledCVT() 1412 } 1413 if i < 0 || len(h.scaledCVT) <= int(i) { 1414 return 0 1415 } 1416 return h.scaledCVT[i] 1417 } 1418 1419 // setScaledCVT overrides the scaled value from the font's Control Value Table. 1420 func (h *hinter) setScaledCVT(i int32, v fixed.Int26_6) { 1421 if !h.scaledCVTInitialized { 1422 h.initializeScaledCVT() 1423 } 1424 if i < 0 || len(h.scaledCVT) <= int(i) { 1425 return 1426 } 1427 h.scaledCVT[i] = v 1428 } 1429 1430 func (h *hinter) point(zonePointer uint32, pt pointType, i int32) *Point { 1431 points := h.points[h.gs.zp[zonePointer]][pt] 1432 if i < 0 || len(points) <= int(i) { 1433 return nil 1434 } 1435 return &points[i] 1436 } 1437 1438 func (h *hinter) move(p *Point, distance fixed.Int26_6, touch bool) { 1439 fvx := int64(h.gs.fv[0]) 1440 pvx := int64(h.gs.pv[0]) 1441 if fvx == 0x4000 && pvx == 0x4000 { 1442 p.X += fixed.Int26_6(distance) 1443 if touch { 1444 p.Flags |= flagTouchedX 1445 } 1446 return 1447 } 1448 1449 fvy := int64(h.gs.fv[1]) 1450 pvy := int64(h.gs.pv[1]) 1451 if fvy == 0x4000 && pvy == 0x4000 { 1452 p.Y += fixed.Int26_6(distance) 1453 if touch { 1454 p.Flags |= flagTouchedY 1455 } 1456 return 1457 } 1458 1459 fvDotPv := (fvx*pvx + fvy*pvy) >> 14 1460 1461 if fvx != 0 { 1462 p.X += fixed.Int26_6(mulDiv(fvx, int64(distance), fvDotPv)) 1463 if touch { 1464 p.Flags |= flagTouchedX 1465 } 1466 } 1467 1468 if fvy != 0 { 1469 p.Y += fixed.Int26_6(mulDiv(fvy, int64(distance), fvDotPv)) 1470 if touch { 1471 p.Flags |= flagTouchedY 1472 } 1473 } 1474 } 1475 1476 func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) { 1477 if p1 > p2 { 1478 return 1479 } 1480 if ref1 >= len(h.points[glyphZone][current]) || 1481 ref2 >= len(h.points[glyphZone][current]) { 1482 return 1483 } 1484 1485 var ifu1, ifu2 fixed.Int26_6 1486 if interpY { 1487 ifu1 = h.points[glyphZone][inFontUnits][ref1].Y 1488 ifu2 = h.points[glyphZone][inFontUnits][ref2].Y 1489 } else { 1490 ifu1 = h.points[glyphZone][inFontUnits][ref1].X 1491 ifu2 = h.points[glyphZone][inFontUnits][ref2].X 1492 } 1493 if ifu1 > ifu2 { 1494 ifu1, ifu2 = ifu2, ifu1 1495 ref1, ref2 = ref2, ref1 1496 } 1497 1498 var unh1, unh2, delta1, delta2 fixed.Int26_6 1499 if interpY { 1500 unh1 = h.points[glyphZone][unhinted][ref1].Y 1501 unh2 = h.points[glyphZone][unhinted][ref2].Y 1502 delta1 = h.points[glyphZone][current][ref1].Y - unh1 1503 delta2 = h.points[glyphZone][current][ref2].Y - unh2 1504 } else { 1505 unh1 = h.points[glyphZone][unhinted][ref1].X 1506 unh2 = h.points[glyphZone][unhinted][ref2].X 1507 delta1 = h.points[glyphZone][current][ref1].X - unh1 1508 delta2 = h.points[glyphZone][current][ref2].X - unh2 1509 } 1510 1511 var xy, ifuXY fixed.Int26_6 1512 if ifu1 == ifu2 { 1513 for i := p1; i <= p2; i++ { 1514 if interpY { 1515 xy = h.points[glyphZone][unhinted][i].Y 1516 } else { 1517 xy = h.points[glyphZone][unhinted][i].X 1518 } 1519 1520 if xy <= unh1 { 1521 xy += delta1 1522 } else { 1523 xy += delta2 1524 } 1525 1526 if interpY { 1527 h.points[glyphZone][current][i].Y = xy 1528 } else { 1529 h.points[glyphZone][current][i].X = xy 1530 } 1531 } 1532 return 1533 } 1534 1535 scale, scaleOK := int64(0), false 1536 for i := p1; i <= p2; i++ { 1537 if interpY { 1538 xy = h.points[glyphZone][unhinted][i].Y 1539 ifuXY = h.points[glyphZone][inFontUnits][i].Y 1540 } else { 1541 xy = h.points[glyphZone][unhinted][i].X 1542 ifuXY = h.points[glyphZone][inFontUnits][i].X 1543 } 1544 1545 if xy <= unh1 { 1546 xy += delta1 1547 } else if xy >= unh2 { 1548 xy += delta2 1549 } else { 1550 if !scaleOK { 1551 scaleOK = true 1552 scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1)) 1553 } 1554 numer := int64(ifuXY-ifu1) * scale 1555 if numer >= 0 { 1556 numer += 0x8000 1557 } else { 1558 numer -= 0x8000 1559 } 1560 xy = unh1 + delta1 + fixed.Int26_6(numer/0x10000) 1561 } 1562 1563 if interpY { 1564 h.points[glyphZone][current][i].Y = xy 1565 } else { 1566 h.points[glyphZone][current][i].X = xy 1567 } 1568 } 1569 } 1570 1571 func (h *hinter) iupShift(interpY bool, p1, p2, p int) { 1572 var delta fixed.Int26_6 1573 if interpY { 1574 delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y 1575 } else { 1576 delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X 1577 } 1578 if delta == 0 { 1579 return 1580 } 1581 for i := p1; i < p2; i++ { 1582 if i == p { 1583 continue 1584 } 1585 if interpY { 1586 h.points[glyphZone][current][i].Y += delta 1587 } else { 1588 h.points[glyphZone][current][i].X += delta 1589 } 1590 } 1591 } 1592 1593 func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d fixed.Int26_6, ok bool) { 1594 zonePointer, i = uint32(0), h.gs.rp[1] 1595 if useZP1 { 1596 zonePointer, i = 1, h.gs.rp[2] 1597 } 1598 p := h.point(zonePointer, current, i) 1599 q := h.point(zonePointer, unhinted, i) 1600 if p == nil || q == nil { 1601 return 0, 0, 0, false 1602 } 1603 d = dotProduct(p.X-q.X, p.Y-q.Y, h.gs.pv) 1604 return zonePointer, i, d, true 1605 } 1606 1607 // skipInstructionPayload increments pc by the extra data that follows a 1608 // variable length PUSHB or PUSHW instruction. 1609 func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) { 1610 switch program[pc] { 1611 case opNPUSHB: 1612 pc++ 1613 if pc >= len(program) { 1614 return 0, false 1615 } 1616 pc += int(program[pc]) 1617 case opNPUSHW: 1618 pc++ 1619 if pc >= len(program) { 1620 return 0, false 1621 } 1622 pc += 2 * int(program[pc]) 1623 case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011, 1624 opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111: 1625 pc += int(program[pc] - (opPUSHB000 - 1)) 1626 case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011, 1627 opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111: 1628 pc += 2 * int(program[pc]-(opPUSHW000-1)) 1629 } 1630 return pc, true 1631 } 1632 1633 // f2dot14 is a 2.14 fixed point number. 1634 type f2dot14 int16 1635 1636 func normalize(x, y f2dot14) [2]f2dot14 { 1637 fx, fy := float64(x), float64(y) 1638 l := 0x4000 / math.Hypot(fx, fy) 1639 fx *= l 1640 if fx >= 0 { 1641 fx += 0.5 1642 } else { 1643 fx -= 0.5 1644 } 1645 fy *= l 1646 if fy >= 0 { 1647 fy += 0.5 1648 } else { 1649 fy -= 0.5 1650 } 1651 return [2]f2dot14{f2dot14(fx), f2dot14(fy)} 1652 } 1653 1654 // fabs returns abs(x) in 26.6 fixed point arithmetic. 1655 func fabs(x fixed.Int26_6) fixed.Int26_6 { 1656 if x < 0 { 1657 return -x 1658 } 1659 return x 1660 } 1661 1662 // fdiv returns x/y in 26.6 fixed point arithmetic. 1663 func fdiv(x, y fixed.Int26_6) fixed.Int26_6 { 1664 return fixed.Int26_6((int64(x) << 6) / int64(y)) 1665 } 1666 1667 // fmul returns x*y in 26.6 fixed point arithmetic. 1668 func fmul(x, y fixed.Int26_6) fixed.Int26_6 { 1669 return fixed.Int26_6((int64(x)*int64(y) + 1<<5) >> 6) 1670 } 1671 1672 // dotProduct returns the dot product of [x, y] and q. It is almost the same as 1673 // px := int64(x) 1674 // py := int64(y) 1675 // qx := int64(q[0]) 1676 // qy := int64(q[1]) 1677 // return fixed.Int26_6((px*qx + py*qy + 1<<13) >> 14) 1678 // except that the computation is done with 32-bit integers to produce exactly 1679 // the same rounding behavior as C Freetype. 1680 func dotProduct(x, y fixed.Int26_6, q [2]f2dot14) fixed.Int26_6 { 1681 // Compute x*q[0] as 64-bit value. 1682 l := uint32((int32(x) & 0xFFFF) * int32(q[0])) 1683 m := (int32(x) >> 16) * int32(q[0]) 1684 1685 lo1 := l + (uint32(m) << 16) 1686 hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l) 1687 1688 // Compute y*q[1] as 64-bit value. 1689 l = uint32((int32(y) & 0xFFFF) * int32(q[1])) 1690 m = (int32(y) >> 16) * int32(q[1]) 1691 1692 lo2 := l + (uint32(m) << 16) 1693 hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l) 1694 1695 // Add them. 1696 lo := lo1 + lo2 1697 hi := hi1 + hi2 + bool2int32(lo < lo1) 1698 1699 // Divide the result by 2^14 with rounding. 1700 s := hi >> 31 1701 l = lo + uint32(s) 1702 hi += s + bool2int32(l < lo) 1703 lo = l 1704 1705 l = lo + 0x2000 1706 hi += bool2int32(l < lo) 1707 1708 return fixed.Int26_6((uint32(hi) << 18) | (l >> 14)) 1709 } 1710 1711 // mulDiv returns x*y/z, rounded to the nearest integer. 1712 func mulDiv(x, y, z int64) int64 { 1713 xy := x * y 1714 if z < 0 { 1715 xy, z = -xy, -z 1716 } 1717 if xy >= 0 { 1718 xy += z / 2 1719 } else { 1720 xy -= z / 2 1721 } 1722 return xy / z 1723 } 1724 1725 // round rounds the given number. The rounding algorithm is described at 1726 // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding 1727 func (h *hinter) round(x fixed.Int26_6) fixed.Int26_6 { 1728 if h.gs.roundPeriod == 0 { 1729 // Rounding is off. 1730 return x 1731 } 1732 if x >= 0 { 1733 ret := x - h.gs.roundPhase + h.gs.roundThreshold 1734 if h.gs.roundSuper45 { 1735 ret /= h.gs.roundPeriod 1736 ret *= h.gs.roundPeriod 1737 } else { 1738 ret &= -h.gs.roundPeriod 1739 } 1740 if x != 0 && ret < 0 { 1741 ret = 0 1742 } 1743 return ret + h.gs.roundPhase 1744 } 1745 ret := -x - h.gs.roundPhase + h.gs.roundThreshold 1746 if h.gs.roundSuper45 { 1747 ret /= h.gs.roundPeriod 1748 ret *= h.gs.roundPeriod 1749 } else { 1750 ret &= -h.gs.roundPeriod 1751 } 1752 if ret < 0 { 1753 ret = 0 1754 } 1755 return -ret - h.gs.roundPhase 1756 } 1757 1758 func bool2int32(b bool) int32 { 1759 if b { 1760 return 1 1761 } 1762 return 0 1763 }