...

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

Documentation: github.com/golang/freetype/truetype

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

View as plain text