freetype/truetype: add explicit graphics state.

These new opcodes aren't unit-tested per se, but they will be
exercised by an end-to-end hinting test of the Luxi fonts.

R=bsiegert
CC=golang-dev
https://codereview.appspot.com/12100043
This commit is contained in:
Nigel Tao 2013-08-02 19:13:26 +10:00
parent e3b4bc4c1f
commit 43c4b0b00d
3 changed files with 198 additions and 100 deletions

View File

@ -105,8 +105,9 @@ func (g *GlyphBuf) decodeCoords(d []byte, offset int, np0 int) int {
} }
// Load loads a glyph's contours from a Font, overwriting any previously // Load loads a glyph's contours from a Font, overwriting any previously
// loaded contours for this GlyphBuf. The Hinter is optional; if non-nil, then // loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point
// the resulting glyph will be hinted by the Font's bytecode instructions. // units in 1 em. The Hinter is optional; if non-nil, then the resulting glyph
// will be hinted by the Font's bytecode instructions.
func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h *Hinter) error { func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h *Hinter) error {
// Reset the GlyphBuf. // Reset the GlyphBuf.
g.B = Bounds{} g.B = Bounds{}

View File

@ -34,17 +34,45 @@ type Hinter struct {
font *Font font *Font
scale int32 scale int32
// The fields below constitue the graphics state, which is described at // gs and defaultGS are the current and default graphics state. The
// https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html // default graphics state is the global default graphics state after
// the font's fpgm and prep programs have been run.
gs, defaultGS graphicsState
}
// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
type graphicsState struct {
// Projection vector, freedom vector and dual projection vector. // Projection vector, freedom vector and dual projection vector.
pv, fv, dv [2]f2dot14 pv, fv, dv [2]f2dot14
// Reference points and zone pointers.
rp, zp [3]int32
// Control Value / Single Width Cut-In.
controlValueCutIn, singleWidthCutIn, singleWidth f26dot6
// Delta base / shift.
deltaBase, deltaShift int32
// Minimum distance. // Minimum distance.
minDist f26dot6 minDist f26dot6
// Loop count. // Loop count.
loop int32 loop int32
// Rounding policy. // Rounding policy.
roundPeriod, roundPhase, roundThreshold f26dot6 roundPeriod, roundPhase, roundThreshold f26dot6
// Auto-flip.
autoFlip bool
}
var globalDefaultGS = graphicsState{
pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis.
fv: [2]f2dot14{0x4000, 0},
dv: [2]f2dot14{0x4000, 0},
zp: [3]int32{1, 1, 1},
controlValueCutIn: (17 << 6) / 16, // 17/16 as an f26dot6.
deltaBase: 9,
deltaShift: 3,
minDist: 1 << 6, // 1 as an f26dot6.
loop: 1,
roundPeriod: 1 << 6, // 1 as an f26dot6.
roundThreshold: 1 << 5, // 1/2 as an f26dot6.
autoFlip: true,
} }
func (h *Hinter) init(f *Font, scale int32) error { func (h *Hinter) init(f *Font, scale int32) error {
@ -78,28 +106,29 @@ func (h *Hinter) init(f *Font, scale int32) error {
if rescale { if rescale {
h.scale = scale h.scale = scale
h.defaultGS = globalDefaultGS
if len(f.prep) != 0 { if len(f.prep) != 0 {
if err := h.run(f.prep); err != nil { if err := h.run(f.prep); err != nil {
return err return err
} }
h.defaultGS = h.gs
// The MS rasterizer doesn't allow the following graphics state
// variables to be modified by the CVT program.
h.defaultGS.pv = globalDefaultGS.pv
h.defaultGS.fv = globalDefaultGS.fv
h.defaultGS.dv = globalDefaultGS.dv
h.defaultGS.rp = globalDefaultGS.rp
h.defaultGS.zp = globalDefaultGS.zp
h.defaultGS.loop = globalDefaultGS.loop
} }
} }
return nil return nil
} }
func (h *Hinter) run(program []byte) error { func (h *Hinter) run(program []byte) error {
// The default vectors are along the X axis. h.gs = h.defaultGS
h.pv = [2]f2dot14{0x4000, 0}
h.fv = [2]f2dot14{0x4000, 0}
h.dv = [2]f2dot14{0x4000, 0}
// The default minimum distance is 1.
h.minDist = 1 << 6
// The default loop count is 1.
h.loop = 1
// The default rounding policy is round to grid.
h.roundPeriod = 1 << 6
h.roundPhase = 0
h.roundThreshold = 1 << 5
if len(program) > 50000 { if len(program) > 50000 {
return errors.New("truetype: hinting: too many instructions") return errors.New("truetype: hinting: too many instructions")
@ -127,81 +156,95 @@ func (h *Hinter) run(program []byte) error {
switch opcode { switch opcode {
case opSVTCA0: case opSVTCA0:
h.pv = [2]f2dot14{0, 0x4000} h.gs.pv = [2]f2dot14{0, 0x4000}
h.fv = [2]f2dot14{0, 0x4000} h.gs.fv = [2]f2dot14{0, 0x4000}
// TODO: h.dv = h.pv ?? // TODO: h.gs.dv = h.gs.pv ??
case opSVTCA1: case opSVTCA1:
h.pv = [2]f2dot14{0x4000, 0} h.gs.pv = [2]f2dot14{0x4000, 0}
h.fv = [2]f2dot14{0x4000, 0} h.gs.fv = [2]f2dot14{0x4000, 0}
// TODO: h.dv = h.pv ?? // TODO: h.gs.dv = h.gs.pv ??
case opSPVTCA0: case opSPVTCA0:
h.pv = [2]f2dot14{0, 0x4000} h.gs.pv = [2]f2dot14{0, 0x4000}
// TODO: h.dv = h.pv ?? // TODO: h.gs.dv = h.gs.pv ??
case opSPVTCA1: case opSPVTCA1:
h.pv = [2]f2dot14{0x4000, 0} h.gs.pv = [2]f2dot14{0x4000, 0}
// TODO: h.dv = h.pv ?? // TODO: h.gs.dv = h.gs.pv ??
case opSFVTCA0: case opSFVTCA0:
h.fv = [2]f2dot14{0, 0x4000} h.gs.fv = [2]f2dot14{0, 0x4000}
case opSFVTCA1: case opSFVTCA1:
h.fv = [2]f2dot14{0x4000, 0} h.gs.fv = [2]f2dot14{0x4000, 0}
case opSPVFS: case opSPVFS:
top -= 2 top -= 2
h.pv[0] = f2dot14(h.stack[top+0]) h.gs.pv[0] = f2dot14(h.stack[top+0])
h.pv[1] = f2dot14(h.stack[top+1]) h.gs.pv[1] = f2dot14(h.stack[top+1])
// TODO: normalize h.pv ?? // TODO: normalize h.gs.pv ??
// TODO: h.dv = h.pv ?? // TODO: h.gs.dv = h.gs.pv ??
case opSFVFS: case opSFVFS:
top -= 2 top -= 2
h.fv[0] = f2dot14(h.stack[top+0]) h.gs.fv[0] = f2dot14(h.stack[top+0])
h.fv[1] = f2dot14(h.stack[top+1]) h.gs.fv[1] = f2dot14(h.stack[top+1])
// TODO: normalize h.fv ?? // TODO: normalize h.gs.fv ??
case opGPV: case opGPV:
if top+1 >= len(h.stack) { if top+1 >= len(h.stack) {
return errors.New("truetype: hinting: stack overflow") return errors.New("truetype: hinting: stack overflow")
} }
h.stack[top+0] = int32(h.pv[0]) h.stack[top+0] = int32(h.gs.pv[0])
h.stack[top+1] = int32(h.pv[1]) h.stack[top+1] = int32(h.gs.pv[1])
top += 2 top += 2
case opGFV: case opGFV:
if top+1 >= len(h.stack) { if top+1 >= len(h.stack) {
return errors.New("truetype: hinting: stack overflow") return errors.New("truetype: hinting: stack overflow")
} }
h.stack[top+0] = int32(h.fv[0]) h.stack[top+0] = int32(h.gs.fv[0])
h.stack[top+1] = int32(h.fv[1]) h.stack[top+1] = int32(h.gs.fv[1])
top += 2 top += 2
case opSFVTPV: case opSFVTPV:
h.fv = h.pv h.gs.fv = h.gs.pv
case opSRP0, opSRP1, opSRP2:
top--
h.gs.rp[opcode-opSRP0] = h.stack[top]
case opSZP0, opSZP1, opSZP2:
top--
h.gs.zp[opcode-opSZP0] = h.stack[top]
case opSZPS:
top--
h.gs.zp[0] = h.stack[top]
h.gs.zp[1] = h.stack[top]
h.gs.zp[2] = h.stack[top]
case opSLOOP: case opSLOOP:
top-- top--
if h.stack[top] <= 0 { if h.stack[top] <= 0 {
return errors.New("truetype: hinting: invalid data") return errors.New("truetype: hinting: invalid data")
} }
h.loop = h.stack[top] h.gs.loop = h.stack[top]
case opRTG: case opRTG:
h.roundPeriod = 1 << 6 h.gs.roundPeriod = 1 << 6
h.roundPhase = 0 h.gs.roundPhase = 0
h.roundThreshold = 1 << 5 h.gs.roundThreshold = 1 << 5
case opRTHG: case opRTHG:
h.roundPeriod = 1 << 6 h.gs.roundPeriod = 1 << 6
h.roundPhase = 1 << 5 h.gs.roundPhase = 1 << 5
h.roundThreshold = 1 << 5 h.gs.roundThreshold = 1 << 5
case opSMD: case opSMD:
top-- top--
h.minDist = f26dot6(h.stack[top]) h.gs.minDist = f26dot6(h.stack[top])
case opELSE: case opELSE:
opcode = 1 opcode = 1
@ -212,6 +255,18 @@ func (h *Hinter) run(program []byte) error {
pc += int(h.stack[top]) pc += int(h.stack[top])
continue continue
case opSCVTCI:
top--
h.gs.controlValueCutIn = f26dot6(h.stack[top])
case opSSWCI:
top--
h.gs.singleWidthCutIn = f26dot6(h.stack[top])
case opSSW:
top--
h.gs.singleWidth = f26dot6(h.stack[top])
case opDUP: case opDUP:
if top >= len(h.stack) { if top >= len(h.stack) {
return errors.New("truetype: hinting: stack overflow") return errors.New("truetype: hinting: stack overflow")
@ -306,9 +361,9 @@ func (h *Hinter) run(program []byte) error {
program, pc = callStack[callStackTop].program, callStack[callStackTop].pc program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
case opRTDG: case opRTDG:
h.roundPeriod = 1 << 5 h.gs.roundPeriod = 1 << 5
h.roundPhase = 0 h.gs.roundPhase = 0
h.roundThreshold = 1 << 4 h.gs.roundThreshold = 1 << 4
case opNPUSHB: case opNPUSHB:
opcode = 0 opcode = 0
@ -333,6 +388,17 @@ func (h *Hinter) run(program []byte) error {
} }
h.stack[top-1] = h.store[i] h.stack[top-1] = h.store[i]
case opMPPEM, opMPS:
if top >= len(h.stack) {
return errors.New("truetype: hinting: stack overflow")
}
// For MPS, point size should be irrelevant; we return the PPEM.
h.stack[top] = h.scale >> 6
top++
case opFLIPON, opFLIPOFF:
h.gs.autoFlip = opcode == opFLIPON
case opDEBUG: case opDEBUG:
// No-op. // No-op.
@ -385,6 +451,14 @@ func (h *Hinter) run(program []byte) error {
case opNOT: case opNOT:
h.stack[top-1] = bool2int32(h.stack[top-1] == 0) h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
case opSDB:
top--
h.gs.deltaBase = h.stack[top]
case opSDS:
top--
h.gs.deltaShift = h.stack[top]
case opADD: case opADD:
top-- top--
h.stack[top-1] += h.stack[top] h.stack[top-1] += h.stack[top]
@ -435,23 +509,23 @@ func (h *Hinter) run(program []byte) error {
top-- top--
switch (h.stack[top] >> 6) & 0x03 { switch (h.stack[top] >> 6) & 0x03 {
case 0: case 0:
h.roundPeriod = 1 << 5 h.gs.roundPeriod = 1 << 5
case 1, 3: case 1, 3:
h.roundPeriod = 1 << 6 h.gs.roundPeriod = 1 << 6
case 2: case 2:
h.roundPeriod = 1 << 7 h.gs.roundPeriod = 1 << 7
} }
if opcode == opS45ROUND { if opcode == opS45ROUND {
// The spec says to multiply by √2, but the C Freetype code says 1/√2. // The spec says to multiply by √2, but the C Freetype code says 1/√2.
// We go with 1/√2. // We go with 1/√2.
h.roundPeriod *= 46341 h.gs.roundPeriod *= 46341
h.roundPeriod /= 65536 h.gs.roundPeriod /= 65536
} }
h.roundPhase = h.roundPeriod * f26dot6((h.stack[top]>>4)&0x03) / 4 h.gs.roundPhase = h.gs.roundPeriod * f26dot6((h.stack[top]>>4)&0x03) / 4
if x := h.stack[top] & 0x0f; x != 0 { if x := h.stack[top] & 0x0f; x != 0 {
h.roundThreshold = h.roundPeriod * f26dot6(x-4) / 8 h.gs.roundThreshold = h.gs.roundPeriod * f26dot6(x-4) / 8
} else { } else {
h.roundThreshold = h.roundPeriod - 1 h.gs.roundThreshold = h.gs.roundPeriod - 1
} }
case opJROT: case opJROT:
@ -469,24 +543,43 @@ func (h *Hinter) run(program []byte) error {
} }
case opROFF: case opROFF:
h.roundPeriod = 0 h.gs.roundPeriod = 0
h.roundPhase = 0 h.gs.roundPhase = 0
h.roundThreshold = 0 h.gs.roundThreshold = 0
case opRUTG: case opRUTG:
h.roundPeriod = 1 << 6 h.gs.roundPeriod = 1 << 6
h.roundPhase = 0 h.gs.roundPhase = 0
h.roundThreshold = 1<<6 - 1 h.gs.roundThreshold = 1<<6 - 1
case opRDTG: case opRDTG:
h.roundPeriod = 1 << 6 h.gs.roundPeriod = 1 << 6
h.roundPhase = 0 h.gs.roundPhase = 0
h.roundThreshold = 0 h.gs.roundThreshold = 0
case opSANGW, opAA: case opSANGW, opAA:
// These ops are "anachronistic" and no longer used. // These ops are "anachronistic" and no longer used.
top-- top--
case opSCANCTRL:
// We do not support dropout control, as we always rasterize grayscale glyphs.
top--
case opGETINFO:
res := int32(0)
if h.stack[top-1]&(1<<0) != 0 {
// Set the engine version. We hard-code this to 35, the same as
// the C freetype code, which says that "Version~35 corresponds
// to MS rasterizer v.1.7 as used e.g. in Windows~98".
res |= 35
}
if h.stack[top-1]&(1<<5) != 0 {
// Set that we support grayscale.
res |= 1 << 12
}
// We set no other bits, as we do not support rotated or stretched glyphs.
h.stack[top-1] = res
case opIDEF: case opIDEF:
// IDEF is for ancient versions of the bytecode interpreter, and is no longer used. // IDEF is for ancient versions of the bytecode interpreter, and is no longer used.
return errors.New("truetype: hinting: unsupported IDEF instruction") return errors.New("truetype: hinting: unsupported IDEF instruction")
@ -507,6 +600,10 @@ func (h *Hinter) run(program []byte) error {
h.stack[top-1] = h.stack[top] h.stack[top-1] = h.stack[top]
} }
case opSCANTYPE:
// We do not support dropout control, as we always rasterize grayscale glyphs.
top--
case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011, case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011,
opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111: opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
@ -646,26 +743,26 @@ func (x f26dot6) mul(y f26dot6) f26dot6 {
// round rounds the given number. The rounding algorithm is described at // round rounds the given number. The rounding algorithm is described at
// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
func (h *Hinter) round(x f26dot6) f26dot6 { func (h *Hinter) round(x f26dot6) f26dot6 {
if h.roundPeriod == 0 { if h.gs.roundPeriod == 0 {
return x return x
} }
neg := x < 0 neg := x < 0
x -= h.roundPhase x -= h.gs.roundPhase
x += h.roundThreshold x += h.gs.roundThreshold
if x >= 0 { if x >= 0 {
x = (x / h.roundPeriod) * h.roundPeriod x = (x / h.gs.roundPeriod) * h.gs.roundPeriod
} else { } else {
x -= h.roundPeriod x -= h.gs.roundPeriod
x += 1 x += 1
x = (x / h.roundPeriod) * h.roundPeriod x = (x / h.gs.roundPeriod) * h.gs.roundPeriod
} }
x += h.roundPhase x += h.gs.roundPhase
if neg { if neg {
if x >= 0 { if x >= 0 {
x = h.roundPhase - h.roundPeriod x = h.gs.roundPhase - h.gs.roundPeriod
} }
} else if x < 0 { } else if x < 0 {
x = h.roundPhase x = h.gs.roundPhase
} }
return x return x
} }

View File

@ -26,22 +26,22 @@ const (
opGFV = 0x0d // Get Freedom Vector opGFV = 0x0d // Get Freedom Vector
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
opISECT = 0x0f opISECT = 0x0f
opSRP0 = 0x10 opSRP0 = 0x10 // Set Reference Point 0
opSRP1 = 0x11 opSRP1 = 0x11 // Set Reference Point 1
opSRP2 = 0x12 opSRP2 = 0x12 // Set Reference Point 2
opSZP0 = 0x13 opSZP0 = 0x13 // Set Zone Pointer 0
opSZP1 = 0x14 opSZP1 = 0x14 // Set Zone Pointer 1
opSZP2 = 0x15 opSZP2 = 0x15 // Set Zone Pointer 2
opSZPS = 0x16 opSZPS = 0x16 // Set Zone PointerS
opSLOOP = 0x17 // Set LOOP variable opSLOOP = 0x17 // Set LOOP variable
opRTG = 0x18 // Round To Grid opRTG = 0x18 // Round To Grid
opRTHG = 0x19 // Round To Half Grid opRTHG = 0x19 // Round To Half Grid
opSMD = 0x1a // Set Minimum Distance opSMD = 0x1a // Set Minimum Distance
opELSE = 0x1b // ELSE clause opELSE = 0x1b // ELSE clause
opJMPR = 0x1c // JuMP Relative opJMPR = 0x1c // JuMP Relative
opSCVTCI = 0x1d opSCVTCI = 0x1d // Set Control Value Table Cut-In
opSSWCI = 0x1e opSSWCI = 0x1e // Set Single Width Cut-In
opSSW = 0x1f opSSW = 0x1f // Set Single Width
opDUP = 0x20 // DUPlicate top stack element opDUP = 0x20 // DUPlicate top stack element
opPOP = 0x21 // POP top stack element opPOP = 0x21 // POP top stack element
opCLEAR = 0x22 // CLEAR the stack opCLEAR = 0x22 // CLEAR the stack
@ -85,10 +85,10 @@ const (
opSCFS = 0x48 opSCFS = 0x48
opMD0 = 0x49 opMD0 = 0x49
opMD1 = 0x4a opMD1 = 0x4a
opMPPEM = 0x4b opMPPEM = 0x4b // Measure Pixels Per EM
opMPS = 0x4c opMPS = 0x4c // Measure Point Size
opFLIPON = 0x4d opFLIPON = 0x4d // set the auto FLIP Boolean to ON
opFLIPOFF = 0x4e opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
opDEBUG = 0x4f // DEBUG call opDEBUG = 0x4f // DEBUG call
opLT = 0x50 // Less Than opLT = 0x50 // Less Than
opLTEQ = 0x51 // Less Than or EQual opLTEQ = 0x51 // Less Than or EQual
@ -104,8 +104,8 @@ const (
opOR = 0x5b // logical OR opOR = 0x5b // logical OR
opNOT = 0x5c // logical NOT opNOT = 0x5c // logical NOT
opDELTAP1 = 0x5d opDELTAP1 = 0x5d
opSDB = 0x5e opSDB = 0x5e // Set Delta Base in the graphics state
opSDS = 0x5f opSDS = 0x5f // Set Delta Shift in the graphics state
opADD = 0x60 // ADD opADD = 0x60 // ADD
opSUB = 0x61 // SUBtract opSUB = 0x61 // SUBtract
opDIV = 0x62 // DIVide opDIV = 0x62 // DIVide
@ -143,15 +143,15 @@ const (
opFLIPRGOFF = 0x82 opFLIPRGOFF = 0x82
op_0x83 = 0x83 op_0x83 = 0x83
op_0x84 = 0x84 op_0x84 = 0x84
opSCANCTRL = 0x85 opSCANCTRL = 0x85 // SCAN conversion ConTRoL
opSDPVTL0 = 0x86 opSDPVTL0 = 0x86
opSDPVTL1 = 0x87 opSDPVTL1 = 0x87
opGETINFO = 0x88 opGETINFO = 0x88 // GET INFOrmation
opIDEF = 0x89 // Instruction DEFinition opIDEF = 0x89 // Instruction DEFinition
opROLL = 0x8a // ROLL the top three stack elements opROLL = 0x8a // ROLL the top three stack elements
opMAX = 0x8b // MAXimum of top two stack elements opMAX = 0x8b // MAXimum of top two stack elements
opMIN = 0x8c // MINimum of top two stack elements opMIN = 0x8c // MINimum of top two stack elements
opSCANTYPE = 0x8d opSCANTYPE = 0x8d // SCANTYPE
opINSTCTRL = 0x8e opINSTCTRL = 0x8e
op_0x8f = 0x8f op_0x8f = 0x8f
op_0x90 = 0x90 op_0x90 = 0x90
@ -272,14 +272,14 @@ const (
var popCount = [256]uint8{ var popCount = [256]uint8{
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
0, 0, 0, 0, 0, 0, q, q, q, q, 2, 2, 0, 0, 0, q, // 0x00 - 0x0f 0, 0, 0, 0, 0, 0, q, q, q, q, 2, 2, 0, 0, 0, q, // 0x00 - 0x0f
q, q, q, q, q, q, q, 1, 0, 0, 1, 0, 1, q, q, q, // 0x10 - 0x1f 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
1, 1, 0, 2, 0, 1, 1, q, q, q, 2, 1, 1, 0, q, q, // 0x20 - 0x2f 1, 1, 0, 2, 0, 1, 1, q, q, q, 2, 1, 1, 0, q, q, // 0x20 - 0x2f
q, q, q, q, q, q, q, q, q, q, q, q, q, 0, q, q, // 0x30 - 0x3f q, q, q, q, q, q, q, q, q, q, q, q, q, 0, q, q, // 0x30 - 0x3f
0, 0, 2, 1, q, q, q, q, q, q, q, q, q, q, q, 0, // 0x40 - 0x4f 0, 0, 2, 1, q, q, q, q, q, q, q, 0, 0, 0, 0, 0, // 0x40 - 0x4f
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, q, q, q, // 0x50 - 0x5f 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, q, 1, 1, // 0x50 - 0x5f
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
q, q, q, q, q, q, 1, 1, 2, 2, 0, q, 0, 0, 1, 1, // 0x70 - 0x7f q, q, q, q, q, q, 1, 1, 2, 2, 0, q, 0, 0, 1, 1, // 0x70 - 0x7f
q, q, q, q, q, q, q, q, q, 1, 3, 2, 2, q, q, q, // 0x80 - 0x8f q, q, q, q, q, 1, q, q, 1, 1, 3, 2, 2, 1, q, q, // 0x80 - 0x8f
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x90 - 0x9f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x90 - 0x9f
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xa0 - 0xaf q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xa0 - 0xaf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf