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  }