1
2
3
4
5
6 package truetype
7
8
9
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
34 type callStackEntry struct {
35 program []byte
36 pc int
37 loopCount int32
38 }
39
40
41
42 type hinter struct {
43 stack, store []int32
44
45
46 functions map[int32][]byte
47
48
49
50
51 font *Font
52 scale fixed.Int26_6
53
54
55
56
57 gs, defaultGS graphicsState
58
59
60
61 points [numZone][numPointType][]Point
62 ends []int
63
64
65 scaledCVTInitialized bool
66 scaledCVT []fixed.Int26_6
67 }
68
69
70 type graphicsState struct {
71
72 pv, fv, dv [2]f2dot14
73
74 rp, zp [3]int32
75
76 controlValueCutIn, singleWidthCutIn, singleWidth fixed.Int26_6
77
78 deltaBase, deltaShift int32
79
80 minDist fixed.Int26_6
81
82 loop int32
83
84 roundPeriod, roundPhase, roundThreshold fixed.Int26_6
85 roundSuper45 bool
86
87 autoFlip bool
88 }
89
90 var globalDefaultGS = graphicsState{
91 pv: [2]f2dot14{0x4000, 0},
92 fv: [2]f2dot14{0x4000, 0},
93 dv: [2]f2dot14{0x4000, 0},
94 zp: [3]int32{1, 1, 1},
95 controlValueCutIn: (17 << 6) / 16,
96 deltaBase: 9,
97 deltaShift: 3,
98 minDist: 1 << 6,
99 loop: 1,
100 roundPeriod: 1 << 6,
101 roundThreshold: 1 << 5,
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
163
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
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
301
302
303
304
305
306
307
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
345
346
347
348
349
350
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
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
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
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
607
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
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
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
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
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
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
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
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
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
935
936 h.stack[top-1] = int32(h.round(fixed.Int26_6(h.stack[top-1])))
937
938 case opNROUND00, opNROUND01, opNROUND10, opNROUND11:
939
940
941
942
943
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
965
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
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
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
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
1076
1077
1078 res |= 35
1079 }
1080 if h.stack[top-1]&(1<<5) != 0 {
1081
1082 res |= 1 << 12
1083 }
1084
1085 h.stack[top-1] = res
1086
1087 case opIDEF:
1088
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
1109 top--
1110
1111 case opINSTCTRL:
1112
1113
1114
1115
1116
1117
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
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
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
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
1169
1170 distance := oldDist
1171 if opcode&0x04 != 0 {
1172 distance = h.round(oldDist)
1173 }
1174
1175
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
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
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
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
1215
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
1238
1239 distance := cvtDist
1240 if opcode&0x04 != 0 {
1241
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
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
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
1271 h.move(p, distance-curDist, true)
1272 }
1273 }
1274 pc++
1275 continue
1276
1277 ifelse:
1278
1279
1280
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
1314
1315
1316
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
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
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
1615
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
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
1662 func fabs(x fixed.Int26_6) fixed.Int26_6 {
1663 if x < 0 {
1664 return -x
1665 }
1666 return x
1667 }
1668
1669
1670 func fdiv(x, y fixed.Int26_6) fixed.Int26_6 {
1671 return fixed.Int26_6((int64(x) << 6) / int64(y))
1672 }
1673
1674
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
1680
1681
1682
1683
1684
1685
1686
1687 func dotProduct(x, y fixed.Int26_6, q [2]f2dot14) fixed.Int26_6 {
1688
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
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
1703 lo := lo1 + lo2
1704 hi := hi1 + hi2 + bool2int32(lo < lo1)
1705
1706
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
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
1733
1734 func (h *hinter) round(x fixed.Int26_6) fixed.Int26_6 {
1735 if h.gs.roundPeriod == 0 {
1736
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