1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package vector
6
7import (
8	"bytes"
9	"fmt"
10	"math"
11	"math/rand"
12	"runtime"
13	"testing"
14)
15
16// TestDivideByFFFF tests that dividing by 0xffff is equivalent to multiplying
17// and then shifting by magic constants. The Go compiler itself issues this
18// multiply-and-shift for a division by the constant value 0xffff. This trick
19// is used in the asm code as the GOARCH=amd64 SIMD instructions have parallel
20// multiply but not parallel divide.
21//
22// There's undoubtedly a justification somewhere in Hacker's Delight chapter 10
23// "Integer Division by Constants", but I don't have a more specific link.
24//
25// http://www.hackersdelight.org/divcMore.pdf and
26// http://www.hackersdelight.org/magic.htm
27func TestDivideByFFFF(t *testing.T) {
28	const mul, shift = 0x80008001, 47
29	rng := rand.New(rand.NewSource(1))
30	for i := 0; i < 20000; i++ {
31		u := rng.Uint32()
32		got := uint32((uint64(u) * mul) >> shift)
33		want := u / 0xffff
34		if got != want {
35			t.Fatalf("i=%d, u=%#08x: got %#08x, want %#08x", i, u, got, want)
36		}
37	}
38}
39
40// TestXxxSIMDUnaligned tests that unaligned SIMD loads/stores don't crash.
41
42func TestFixedAccumulateSIMDUnaligned(t *testing.T) {
43	if !haveFixedAccumulateSIMD {
44		t.Skip("No SIMD implemention")
45	}
46
47	dst := make([]uint8, 64)
48	src := make([]uint32, 64)
49	for d := 0; d < 16; d++ {
50		for s := 0; s < 16; s++ {
51			fixedAccumulateOpSrcSIMD(dst[d:d+32], src[s:s+32])
52		}
53	}
54}
55
56func TestFloatingAccumulateSIMDUnaligned(t *testing.T) {
57	if !haveFloatingAccumulateSIMD {
58		t.Skip("No SIMD implemention")
59	}
60
61	dst := make([]uint8, 64)
62	src := make([]float32, 64)
63	for d := 0; d < 16; d++ {
64		for s := 0; s < 16; s++ {
65			floatingAccumulateOpSrcSIMD(dst[d:d+32], src[s:s+32])
66		}
67	}
68}
69
70// TestXxxSIMDShortDst tests that the SIMD implementations don't write past the
71// end of the dst buffer.
72
73func TestFixedAccumulateSIMDShortDst(t *testing.T) {
74	if !haveFixedAccumulateSIMD {
75		t.Skip("No SIMD implemention")
76	}
77
78	const oneQuarter = uint32(int2ϕ(fxOne*fxOne)) / 4
79	src := []uint32{oneQuarter, oneQuarter, oneQuarter, oneQuarter}
80	for i := 0; i < 4; i++ {
81		dst := make([]uint8, 4)
82		fixedAccumulateOpSrcSIMD(dst[:i], src[:i])
83		for j := range dst {
84			if j < i {
85				if got := dst[j]; got == 0 {
86					t.Errorf("i=%d, j=%d: got %#02x, want non-zero", i, j, got)
87				}
88			} else {
89				if got := dst[j]; got != 0 {
90					t.Errorf("i=%d, j=%d: got %#02x, want zero", i, j, got)
91				}
92			}
93		}
94	}
95}
96
97func TestFloatingAccumulateSIMDShortDst(t *testing.T) {
98	if !haveFloatingAccumulateSIMD {
99		t.Skip("No SIMD implemention")
100	}
101
102	const oneQuarter = 0.25
103	src := []float32{oneQuarter, oneQuarter, oneQuarter, oneQuarter}
104	for i := 0; i < 4; i++ {
105		dst := make([]uint8, 4)
106		floatingAccumulateOpSrcSIMD(dst[:i], src[:i])
107		for j := range dst {
108			if j < i {
109				if got := dst[j]; got == 0 {
110					t.Errorf("i=%d, j=%d: got %#02x, want non-zero", i, j, got)
111				}
112			} else {
113				if got := dst[j]; got != 0 {
114					t.Errorf("i=%d, j=%d: got %#02x, want zero", i, j, got)
115				}
116			}
117		}
118	}
119}
120
121func TestFixedAccumulateOpOverShort(t *testing.T)    { testAcc(t, fxInShort, fxMaskShort, "over") }
122func TestFixedAccumulateOpSrcShort(t *testing.T)     { testAcc(t, fxInShort, fxMaskShort, "src") }
123func TestFixedAccumulateMaskShort(t *testing.T)      { testAcc(t, fxInShort, fxMaskShort, "mask") }
124func TestFloatingAccumulateOpOverShort(t *testing.T) { testAcc(t, flInShort, flMaskShort, "over") }
125func TestFloatingAccumulateOpSrcShort(t *testing.T)  { testAcc(t, flInShort, flMaskShort, "src") }
126func TestFloatingAccumulateMaskShort(t *testing.T)   { testAcc(t, flInShort, flMaskShort, "mask") }
127
128func TestFixedAccumulateOpOver16(t *testing.T)    { testAcc(t, fxIn16, fxMask16, "over") }
129func TestFixedAccumulateOpSrc16(t *testing.T)     { testAcc(t, fxIn16, fxMask16, "src") }
130func TestFixedAccumulateMask16(t *testing.T)      { testAcc(t, fxIn16, fxMask16, "mask") }
131func TestFloatingAccumulateOpOver16(t *testing.T) { testAcc(t, flIn16, flMask16, "over") }
132func TestFloatingAccumulateOpSrc16(t *testing.T)  { testAcc(t, flIn16, flMask16, "src") }
133func TestFloatingAccumulateMask16(t *testing.T)   { testAcc(t, flIn16, flMask16, "mask") }
134
135func testAcc(t *testing.T, in interface{}, mask []uint32, op string) {
136	for _, simd := range []bool{false, true} {
137		maxN := 0
138		switch in := in.(type) {
139		case []uint32:
140			if simd && !haveFixedAccumulateSIMD {
141				continue
142			}
143			maxN = len(in)
144		case []float32:
145			if simd && !haveFloatingAccumulateSIMD {
146				continue
147			}
148			maxN = len(in)
149		}
150
151		for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
152			33, 55, 79, 96, 120, 165, 256, maxN} {
153
154			if n > maxN {
155				continue
156			}
157
158			var (
159				got8, want8   []uint8
160				got32, want32 []uint32
161			)
162			switch op {
163			case "over":
164				const background = 0x40
165				got8 = make([]uint8, n)
166				for i := range got8 {
167					got8[i] = background
168				}
169				want8 = make([]uint8, n)
170				for i := range want8 {
171					dstA := uint32(background * 0x101)
172					maskA := mask[i]
173					outA := dstA*(0xffff-maskA)/0xffff + maskA
174					want8[i] = uint8(outA >> 8)
175				}
176
177			case "src":
178				got8 = make([]uint8, n)
179				want8 = make([]uint8, n)
180				for i := range want8 {
181					want8[i] = uint8(mask[i] >> 8)
182				}
183
184			case "mask":
185				got32 = make([]uint32, n)
186				want32 = mask[:n]
187			}
188
189			switch in := in.(type) {
190			case []uint32:
191				switch op {
192				case "over":
193					if simd {
194						fixedAccumulateOpOverSIMD(got8, in[:n])
195					} else {
196						fixedAccumulateOpOver(got8, in[:n])
197					}
198				case "src":
199					if simd {
200						fixedAccumulateOpSrcSIMD(got8, in[:n])
201					} else {
202						fixedAccumulateOpSrc(got8, in[:n])
203					}
204				case "mask":
205					copy(got32, in[:n])
206					if simd {
207						fixedAccumulateMaskSIMD(got32)
208					} else {
209						fixedAccumulateMask(got32)
210					}
211				}
212			case []float32:
213				switch op {
214				case "over":
215					if simd {
216						floatingAccumulateOpOverSIMD(got8, in[:n])
217					} else {
218						floatingAccumulateOpOver(got8, in[:n])
219					}
220				case "src":
221					if simd {
222						floatingAccumulateOpSrcSIMD(got8, in[:n])
223					} else {
224						floatingAccumulateOpSrc(got8, in[:n])
225					}
226				case "mask":
227					if simd {
228						floatingAccumulateMaskSIMD(got32, in[:n])
229					} else {
230						floatingAccumulateMask(got32, in[:n])
231					}
232				}
233			}
234
235			if op != "mask" {
236				if !bytes.Equal(got8, want8) {
237					t.Errorf("simd=%t, n=%d:\ngot:  % x\nwant: % x", simd, n, got8, want8)
238				}
239			} else {
240				if !uint32sMatch(got32, want32) {
241					t.Errorf("simd=%t, n=%d:\ngot:  % x\nwant: % x", simd, n, got32, want32)
242				}
243			}
244		}
245	}
246}
247
248// This package contains multiple implementations of the same algorithm, e.g.
249// there are both SIMD and non-SIMD (vanilla) implementations on GOARCH=amd64.
250// In general, the tests in this file check that the output is *exactly* the
251// same, regardless of implementation.
252//
253// On GOARCH=wasm, float32 arithmetic is done with 64 bit precision. This is
254// allowed by the Go specification: only explicit conversions to float32 have
255// to round to 32 bit precision. However, the vanilla implementation therefore
256// produces different output for GOARCH=wasm than on other GOARCHes.
257//
258// We therefore treat GOARCH=wasm as a special case, where the tests check that
259// the output is only *approximately* the same (within a 0.1% tolerance).
260//
261// It's not that, on GOARCH=wasm, we produce the "wrong" answer. In fact, the
262// computation is more, not less, accurate on GOARCH=wasm. It's that the golden
263// output that the tests compare to were, for historical reasons, produced on
264// GOARCH=amd64 and so done with less accuracy (where float32 arithmetic is
265// performed entirely with 32 bits, not with 64 bits and then rounded back to
266// 32 bits). Furthermore, on amd64, we still want to test that SIMD and
267// non-SIMD produce exactly the same (albeit less accurate) output. The SIMD
268// implementation in particular is limited by what the underlying hardware
269// instructions provide, which often favors speed over accuracy.
270
271// approxEquals returns whether got is within 0.1% of want.
272func approxEquals(got, want float64) bool {
273	const tolerance = 0.001
274	return math.Abs(got-want) <= math.Abs(want)*tolerance
275}
276
277// sixteen is used by TestFloat32ArithmeticWithinTolerance, below. It needs to
278// be a package-level variable so that the compiler does not replace the
279// calculation with a single constant.
280var sixteen float32 = 16
281
282// TestFloat32ArithmeticWithinTolerance checks that approxEquals' tolerance is
283// sufficiently high so that the results of two separate ways of computing the
284// arbitrary fraction 16 / 1122 are deemed "approximately equal" even if they
285// aren't "exactly equal".
286//
287// We're not testing whether the computation on amd64 or wasm is "right" or
288// "wrong". We're testing that we cope with them being different.
289//
290// On GOARCH=amd64, printing x and y gives:
291//   0.0142602495543672
292//   0.014260249212384224
293//
294// On GOARCH=wasm,  printing x and y gives:
295//   0.0142602495543672
296//   0.0142602495543672
297//
298// The infinitely precise (mathematical) answer is:
299//   0.014260249554367201426024955436720142602495543672recurring...
300// See https://play.golang.org/p/RxzKSdD_suE
301//
302// This test establishes a lower bound on approxEquals' tolerance constant.
303// Passing this one test (on all of the various supported GOARCH's) is a
304// necessary but not a sufficient condition on that value. Other tests in this
305// package that call uint32sMatch or float32sMatch (such as TestMakeFxInXxx,
306// TestMakeFlInXxx or anything calling testAcc) also require a sufficiently
307// large tolerance. But those tests are more complicated, and if there is a
308// problem with the tolerance constant, debugging this test can be simpler.
309func TestFloat32ArithmeticWithinTolerance(t *testing.T) {
310	x := float64(sixteen) / 1122 // Always use 64-bit division.
311	y := float64(sixteen / 1122) // Use 32- or 64-bit division (GOARCH dependent).
312	if !approxEquals(x, y) {
313		t.Errorf("x and y were not approximately equal:\nx = %v\ny = %v", x, y)
314	}
315}
316
317func uint32sMatch(xs, ys []uint32) bool {
318	if len(xs) != len(ys) {
319		return false
320	}
321	if runtime.GOARCH == "wasm" {
322		for i := range xs {
323			if !approxEquals(float64(xs[i]), float64(ys[i])) {
324				return false
325			}
326		}
327	} else {
328		for i := range xs {
329			if xs[i] != ys[i] {
330				return false
331			}
332		}
333	}
334	return true
335}
336
337func float32sMatch(xs, ys []float32) bool {
338	if len(xs) != len(ys) {
339		return false
340	}
341	if runtime.GOARCH == "wasm" {
342		for i := range xs {
343			if !approxEquals(float64(xs[i]), float64(ys[i])) {
344				return false
345			}
346		}
347	} else {
348		for i := range xs {
349			if xs[i] != ys[i] {
350				return false
351			}
352		}
353	}
354	return true
355}
356
357func BenchmarkFixedAccumulateOpOver16(b *testing.B)        { benchAcc(b, fxIn16, "over", false) }
358func BenchmarkFixedAccumulateOpOverSIMD16(b *testing.B)    { benchAcc(b, fxIn16, "over", true) }
359func BenchmarkFixedAccumulateOpSrc16(b *testing.B)         { benchAcc(b, fxIn16, "src", false) }
360func BenchmarkFixedAccumulateOpSrcSIMD16(b *testing.B)     { benchAcc(b, fxIn16, "src", true) }
361func BenchmarkFixedAccumulateMask16(b *testing.B)          { benchAcc(b, fxIn16, "mask", false) }
362func BenchmarkFixedAccumulateMaskSIMD16(b *testing.B)      { benchAcc(b, fxIn16, "mask", true) }
363func BenchmarkFloatingAccumulateOpOver16(b *testing.B)     { benchAcc(b, flIn16, "over", false) }
364func BenchmarkFloatingAccumulateOpOverSIMD16(b *testing.B) { benchAcc(b, flIn16, "over", true) }
365func BenchmarkFloatingAccumulateOpSrc16(b *testing.B)      { benchAcc(b, flIn16, "src", false) }
366func BenchmarkFloatingAccumulateOpSrcSIMD16(b *testing.B)  { benchAcc(b, flIn16, "src", true) }
367func BenchmarkFloatingAccumulateMask16(b *testing.B)       { benchAcc(b, flIn16, "mask", false) }
368func BenchmarkFloatingAccumulateMaskSIMD16(b *testing.B)   { benchAcc(b, flIn16, "mask", true) }
369
370func BenchmarkFixedAccumulateOpOver64(b *testing.B)        { benchAcc(b, fxIn64, "over", false) }
371func BenchmarkFixedAccumulateOpOverSIMD64(b *testing.B)    { benchAcc(b, fxIn64, "over", true) }
372func BenchmarkFixedAccumulateOpSrc64(b *testing.B)         { benchAcc(b, fxIn64, "src", false) }
373func BenchmarkFixedAccumulateOpSrcSIMD64(b *testing.B)     { benchAcc(b, fxIn64, "src", true) }
374func BenchmarkFixedAccumulateMask64(b *testing.B)          { benchAcc(b, fxIn64, "mask", false) }
375func BenchmarkFixedAccumulateMaskSIMD64(b *testing.B)      { benchAcc(b, fxIn64, "mask", true) }
376func BenchmarkFloatingAccumulateOpOver64(b *testing.B)     { benchAcc(b, flIn64, "over", false) }
377func BenchmarkFloatingAccumulateOpOverSIMD64(b *testing.B) { benchAcc(b, flIn64, "over", true) }
378func BenchmarkFloatingAccumulateOpSrc64(b *testing.B)      { benchAcc(b, flIn64, "src", false) }
379func BenchmarkFloatingAccumulateOpSrcSIMD64(b *testing.B)  { benchAcc(b, flIn64, "src", true) }
380func BenchmarkFloatingAccumulateMask64(b *testing.B)       { benchAcc(b, flIn64, "mask", false) }
381func BenchmarkFloatingAccumulateMaskSIMD64(b *testing.B)   { benchAcc(b, flIn64, "mask", true) }
382
383func benchAcc(b *testing.B, in interface{}, op string, simd bool) {
384	var f func()
385
386	switch in := in.(type) {
387	case []uint32:
388		if simd && !haveFixedAccumulateSIMD {
389			b.Skip("No SIMD implemention")
390		}
391
392		switch op {
393		case "over":
394			dst := make([]uint8, len(in))
395			if simd {
396				f = func() { fixedAccumulateOpOverSIMD(dst, in) }
397			} else {
398				f = func() { fixedAccumulateOpOver(dst, in) }
399			}
400		case "src":
401			dst := make([]uint8, len(in))
402			if simd {
403				f = func() { fixedAccumulateOpSrcSIMD(dst, in) }
404			} else {
405				f = func() { fixedAccumulateOpSrc(dst, in) }
406			}
407		case "mask":
408			buf := make([]uint32, len(in))
409			copy(buf, in)
410			if simd {
411				f = func() { fixedAccumulateMaskSIMD(buf) }
412			} else {
413				f = func() { fixedAccumulateMask(buf) }
414			}
415		}
416
417	case []float32:
418		if simd && !haveFloatingAccumulateSIMD {
419			b.Skip("No SIMD implemention")
420		}
421
422		switch op {
423		case "over":
424			dst := make([]uint8, len(in))
425			if simd {
426				f = func() { floatingAccumulateOpOverSIMD(dst, in) }
427			} else {
428				f = func() { floatingAccumulateOpOver(dst, in) }
429			}
430		case "src":
431			dst := make([]uint8, len(in))
432			if simd {
433				f = func() { floatingAccumulateOpSrcSIMD(dst, in) }
434			} else {
435				f = func() { floatingAccumulateOpSrc(dst, in) }
436			}
437		case "mask":
438			dst := make([]uint32, len(in))
439			if simd {
440				f = func() { floatingAccumulateMaskSIMD(dst, in) }
441			} else {
442				f = func() { floatingAccumulateMask(dst, in) }
443			}
444		}
445	}
446
447	b.ResetTimer()
448	for i := 0; i < b.N; i++ {
449		f()
450	}
451}
452
453// itou exists because "uint32(int2ϕ(-1))" doesn't compile: constant -1
454// overflows uint32.
455func itou(i int2ϕ) uint32 {
456	return uint32(i)
457}
458
459var fxInShort = []uint32{
460	itou(+0x08000), // +0.125, // Running sum: +0.125
461	itou(-0x20000), // -0.500, // Running sum: -0.375
462	itou(+0x10000), // +0.250, // Running sum: -0.125
463	itou(+0x18000), // +0.375, // Running sum: +0.250
464	itou(+0x08000), // +0.125, // Running sum: +0.375
465	itou(+0x00000), // +0.000, // Running sum: +0.375
466	itou(-0x40000), // -1.000, // Running sum: -0.625
467	itou(-0x20000), // -0.500, // Running sum: -1.125
468	itou(+0x10000), // +0.250, // Running sum: -0.875
469	itou(+0x38000), // +0.875, // Running sum: +0.000
470	itou(+0x10000), // +0.250, // Running sum: +0.250
471	itou(+0x30000), // +0.750, // Running sum: +1.000
472}
473
474var flInShort = []float32{
475	+0.125, // Running sum: +0.125
476	-0.500, // Running sum: -0.375
477	+0.250, // Running sum: -0.125
478	+0.375, // Running sum: +0.250
479	+0.125, // Running sum: +0.375
480	+0.000, // Running sum: +0.375
481	-1.000, // Running sum: -0.625
482	-0.500, // Running sum: -1.125
483	+0.250, // Running sum: -0.875
484	+0.875, // Running sum: +0.000
485	+0.250, // Running sum: +0.250
486	+0.750, // Running sum: +1.000
487}
488
489// It's OK for fxMaskShort and flMaskShort to have slightly different values.
490// Both the fixed and floating point implementations already have (different)
491// rounding errors in the xxxLineTo methods before we get to accumulation. It's
492// OK for 50% coverage (in ideal math) to be approximated by either 0x7fff or
493// 0x8000. Both slices do contain checks that 0% and 100% map to 0x0000 and
494// 0xffff, as does checkCornersCenter in vector_test.go.
495//
496// It is important, though, for the SIMD and non-SIMD fixed point
497// implementations to give the exact same output, and likewise for the floating
498// point implementations.
499
500var fxMaskShort = []uint32{
501	0x2000,
502	0x6000,
503	0x2000,
504	0x4000,
505	0x6000,
506	0x6000,
507	0xa000,
508	0xffff,
509	0xe000,
510	0x0000,
511	0x4000,
512	0xffff,
513}
514
515var flMaskShort = []uint32{
516	0x1fff,
517	0x5fff,
518	0x1fff,
519	0x3fff,
520	0x5fff,
521	0x5fff,
522	0x9fff,
523	0xffff,
524	0xdfff,
525	0x0000,
526	0x3fff,
527	0xffff,
528}
529
530func TestMakeFxInXxx(t *testing.T) {
531	dump := func(us []uint32) string {
532		var b bytes.Buffer
533		for i, u := range us {
534			if i%8 == 0 {
535				b.WriteByte('\n')
536			}
537			fmt.Fprintf(&b, "%#08x, ", u)
538		}
539		return b.String()
540	}
541
542	if !uint32sMatch(fxIn16, hardCodedFxIn16) {
543		t.Errorf("height 16: got:%v\nwant:%v", dump(fxIn16), dump(hardCodedFxIn16))
544	}
545}
546
547func TestMakeFlInXxx(t *testing.T) {
548	dump := func(fs []float32) string {
549		var b bytes.Buffer
550		for i, f := range fs {
551			if i%8 == 0 {
552				b.WriteByte('\n')
553			}
554			fmt.Fprintf(&b, "%v, ", f)
555		}
556		return b.String()
557	}
558
559	if !float32sMatch(flIn16, hardCodedFlIn16) {
560		t.Errorf("height 16: got:%v\nwant:%v", dump(flIn16), dump(hardCodedFlIn16))
561	}
562}
563
564func makeInXxx(height int, useFloatingPointMath bool) *Rasterizer {
565	width, data := scaledBenchmarkGlyphData(height)
566	z := NewRasterizer(width, height)
567	z.setUseFloatingPointMath(useFloatingPointMath)
568	for _, d := range data {
569		switch d.n {
570		case 0:
571			z.MoveTo(d.px, d.py)
572		case 1:
573			z.LineTo(d.px, d.py)
574		case 2:
575			z.QuadTo(d.px, d.py, d.qx, d.qy)
576		}
577	}
578	return z
579}
580
581func makeFxInXxx(height int) []uint32 {
582	z := makeInXxx(height, false)
583	return z.bufU32
584}
585
586func makeFlInXxx(height int) []float32 {
587	z := makeInXxx(height, true)
588	return z.bufF32
589}
590
591// fxInXxx and flInXxx are the z.bufU32 and z.bufF32 inputs to the accumulate
592// functions when rasterizing benchmarkGlyphData at a height of Xxx pixels.
593//
594// fxMaskXxx and flMaskXxx are the corresponding golden outputs of those
595// accumulateMask functions.
596//
597// The hardCodedEtc versions are a sanity check for unexpected changes in the
598// rasterization implementations up to but not including accumulation.
599
600var (
601	fxIn16 = makeFxInXxx(16)
602	fxIn64 = makeFxInXxx(64)
603	flIn16 = makeFlInXxx(16)
604	flIn64 = makeFlInXxx(64)
605)
606
607var hardCodedFxIn16 = []uint32{
608	0x00000000, 0x00000000, 0xffffe91d, 0xfffe7c4a, 0xfffeaa9f, 0xffff4e33, 0xffffc1c5, 0x00007782,
609	0x00009619, 0x0001a857, 0x000129e9, 0x00000028, 0x00000000, 0x00000000, 0xffff6e70, 0xfffd3199,
610	0xffff5ff8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00014b29,
611	0x0002acf3, 0x000007e2, 0xffffca5a, 0xfffcab73, 0xffff8a34, 0x00001b55, 0x0001b334, 0x0001449e,
612	0x0000434d, 0xffff62ec, 0xfffe1443, 0xffff325d, 0x00000000, 0x0002234a, 0x0001dcb6, 0xfffe2948,
613	0xfffdd6b8, 0x00000000, 0x00028cc0, 0x00017340, 0x00000000, 0x00000000, 0x00000000, 0xffffd2d6,
614	0xfffcadd0, 0xffff7f5c, 0x00007400, 0x00038c00, 0xfffe9260, 0xffff2da0, 0x0000023a, 0x0002259b,
615	0x0000182a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffdc600, 0xfffe3a00, 0x00000059,
616	0x0003a44d, 0x00005b59, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
617	0x00000000, 0x00000000, 0xfffe33f3, 0xfffdcc0d, 0x00000000, 0x00033c02, 0x0000c3fe, 0x00000000,
618	0x00000000, 0xffffa13d, 0xfffeeec8, 0xffff8c02, 0xffff8c48, 0xffffc7b5, 0x00000000, 0xffff5b68,
619	0xffff3498, 0x00000000, 0x00033c00, 0x0000c400, 0xffff9bc4, 0xfffdf4a3, 0xfffe8df3, 0xffffe1a8,
620	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00033c00,
621	0x000092c7, 0xfffcf373, 0xffff3dc7, 0x00000fcc, 0x00011ae7, 0x000130c3, 0x0000680d, 0x00004a59,
622	0x00000a20, 0xfffe9dc4, 0xfffe4a3c, 0x00000000, 0x00033c00, 0xfffe87ef, 0xfffe3c11, 0x0000105e,
623	0x0002b9c4, 0x000135dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00,
624	0x00000000, 0x00033c00, 0xfffd9000, 0xffff3400, 0x0000e400, 0x00031c00, 0x00000000, 0x00000000,
625	0x00000000, 0x00000000, 0x00000000, 0xfffe3600, 0xfffdca00, 0x00000000, 0x00033c00, 0xfffcf9a5,
626	0xffffca5b, 0x000120e6, 0x0002df1a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
627	0xfffdb195, 0xfffe4e6b, 0x00000000, 0x00033c00, 0xfffd9e00, 0xffff2600, 0x00002f0e, 0x00033ea3,
628	0x0000924d, 0x00000000, 0x00000000, 0x00000000, 0xfffe83b3, 0xfffd881d, 0xfffff431, 0x00000000,
629	0x00031f60, 0xffff297a, 0xfffdb726, 0x00000000, 0x000053a7, 0x0001b506, 0x0000a24b, 0xffffa32d,
630	0xfffead9b, 0xffff0479, 0xffffffc9, 0x00000000, 0x00000000, 0x0002d800, 0x0001249d, 0xfffd67bb,
631	0xfffe9baa, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000ac03, 0x0001448b,
632	0xfffe0f70, 0x00000000, 0x000229ea, 0x0001d616, 0xffffff8c, 0xfffebf76, 0xfffe54d9, 0xffff5d9e,
633	0xffffd3eb, 0x0000c65e, 0x0000fc15, 0x0001d491, 0xffffb566, 0xfffd9433, 0x00000000, 0x0000e4ec,
634}
635
636var hardCodedFlIn16 = []float32{
637	0, 0, -0.022306755, -0.3782405, -0.33334962, -0.1741521, -0.0607556, 0.11660573,
638	0.14664596, 0.41462868, 0.2907673, 0.0001568835, 0, 0, -0.14239307, -0.7012868,
639	-0.15632017, 0, 0, 0, 0, 0, 0, 0.3230303,
640	0.6690931, 0.007876594, -0.05189419, -0.832786, -0.11531975, 0.026225802, 0.42518616, 0.3154636,
641	0.06598757, -0.15304244, -0.47969276, -0.20012794, 0, 0.5327272, 0.46727282, -0.45950258,
642	-0.5404974, 0, 0.63484025, 0.36515975, 0, 0, 0, -0.04351709,
643	-0.8293345, -0.12714837, 0.11087036, 0.88912964, -0.35792422, -0.2053554, 0.0022513224, 0.5374398,
644	0.023588525, 0, 0, 0, 0, -0.55346966, -0.44653034, 0.0002531938,
645	0.9088273, 0.090919495, 0, 0, 0, 0, 0, 0,
646	0, 0, -0.44745448, -0.5525455, 0, 0.80748945, 0.19251058, 0,
647	0, -0.092476256, -0.2661464, -0.11322958, -0.11298219, -0.055094406, 0, -0.16045958,
648	-0.1996116, 0, 0.80748653, 0.19251347, -0.09804727, -0.51129663, -0.3610403, -0.029615778,
649	0, 0, 0, 0, 0, 0, 0, 0.80748653,
650	0.14411622, -0.76251525, -0.1890875, 0.01527351, 0.27528667, 0.29730347, 0.101477206, 0.07259522,
651	0.009900213, -0.34395567, -0.42788061, 0, 0.80748653, -0.3648737, -0.44261283, 0.015778137,
652	0.6826565, 0.30156538, 0, 0, 0, 0, -0.44563293, -0.55436707,
653	0, 0.80748653, -0.60703933, -0.20044717, 0.22371745, 0.77628255, 0, 0,
654	0, 0, 0, -0.44563293, -0.55436707, 0, 0.80748653, -0.7550391,
655	-0.05244744, 0.2797074, 0.72029257, 0, 0, 0, 0, 0,
656	-0.57440215, -0.42559785, 0, 0.80748653, -0.59273535, -0.21475118, 0.04544862, 0.81148535,
657	0.14306602, 0, 0, 0, -0.369642, -0.61841226, -0.011945802, 0,
658	0.7791623, -0.20691396, -0.57224834, 0, 0.08218567, 0.42637306, 0.1586175, -0.089709565,
659	-0.32935485, -0.24788953, -0.00022224105, 0, 0, 0.7085409, 0.28821066, -0.64765793,
660	-0.34909368, 0, 0, 0, 0, 0, 0.16679136, 0.31914657,
661	-0.48593786, 0, 0.537915, 0.462085, -0.00041967133, -0.3120329, -0.41914812, -0.15886839,
662	-0.042683028, 0.19370951, 0.24624406, 0.45803425, -0.07049577, -0.6091341, 0, 0.22253075,
663}
664
665var fxMask16 = []uint32{
666	0x0000, 0x0000, 0x05b8, 0x66a6, 0xbbfe, 0xe871, 0xf800, 0xda20, 0xb499, 0x4a84, 0x0009, 0x0000, 0x0000,
667	0x0000, 0x2463, 0xd7fd, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad35, 0x01f8, 0x0000,
668	0x0d69, 0xe28c, 0xffff, 0xf92a, 0x8c5d, 0x3b36, 0x2a62, 0x51a7, 0xcc97, 0xffff, 0xffff, 0x772d, 0x0000,
669	0x75ad, 0xffff, 0xffff, 0x5ccf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b4a, 0xdfd6, 0xffff, 0xe2ff, 0x0000,
670	0x5b67, 0x8fff, 0x8f70, 0x060a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8e7f, 0xffff, 0xffe9, 0x16d6,
671	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7303, 0xffff, 0xffff, 0x30ff,
672	0x0000, 0x0000, 0x0000, 0x17b0, 0x5bfe, 0x78fe, 0x95ec, 0xa3fe, 0xa3fe, 0xcd24, 0xfffe, 0xfffe, 0x30fe,
673	0x0001, 0x190d, 0x9be5, 0xf868, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0x30fe,
674	0x0c4c, 0xcf6f, 0xfffe, 0xfc0b, 0xb551, 0x6920, 0x4f1d, 0x3c87, 0x39ff, 0x928e, 0xffff, 0xffff, 0x30ff,
675	0x8f03, 0xffff, 0xfbe7, 0x4d76, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff,
676	0xccff, 0xffff, 0xc6ff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x727f, 0xffff, 0xffff, 0x30ff,
677	0xf296, 0xffff, 0xb7c6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x939a, 0xffff, 0xffff, 0x30ff,
678	0xc97f, 0xffff, 0xf43c, 0x2493, 0x0000, 0x0000, 0x0000, 0x0000, 0x5f13, 0xfd0c, 0xffff, 0xffff, 0x3827,
679	0x6dc9, 0xffff, 0xffff, 0xeb16, 0x7dd4, 0x5541, 0x6c76, 0xc10f, 0xfff1, 0xffff, 0xffff, 0xffff, 0x49ff,
680	0x00d8, 0xa6e9, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xfffe, 0xd4fe, 0x83db, 0xffff, 0xffff, 0x7584,
681	0x0000, 0x001c, 0x503e, 0xbb08, 0xe3a1, 0xeea6, 0xbd0e, 0x7e09, 0x08e5, 0x1b8b, 0xb67f, 0xb67f, 0x7d44,
682}
683
684var flMask16 = []uint32{
685	0x0000, 0x0000, 0x05b5, 0x668a, 0xbbe0, 0xe875, 0xf803, 0xda29, 0xb49f, 0x4a7a, 0x000a, 0x0000, 0x0000,
686	0x0000, 0x2473, 0xd7fb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xad4d, 0x0204, 0x0000,
687	0x0d48, 0xe27a, 0xffff, 0xf949, 0x8c70, 0x3bae, 0x2ac9, 0x51f7, 0xccc4, 0xffff, 0xffff, 0x779f, 0x0000,
688	0x75a1, 0xffff, 0xffff, 0x5d7b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b23, 0xdf73, 0xffff, 0xe39d, 0x0000,
689	0x5ba0, 0x9033, 0x8f9f, 0x0609, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8db0, 0xffff, 0xffef, 0x1746,
690	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x728c, 0xffff, 0xffff, 0x3148,
691	0x0000, 0x0000, 0x0000, 0x17ac, 0x5bce, 0x78cb, 0x95b7, 0xa3d2, 0xa3d2, 0xcce6, 0xffff, 0xffff, 0x3148,
692	0x0000, 0x1919, 0x9bfd, 0xf86b, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x3148,
693	0x0c63, 0xcf97, 0xffff, 0xfc17, 0xb59d, 0x6981, 0x4f87, 0x3cf1, 0x3a68, 0x9276, 0xffff, 0xffff, 0x3148,
694	0x8eb0, 0xffff, 0xfbf5, 0x4d33, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148,
695	0xccaf, 0xffff, 0xc6ba, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7214, 0xffff, 0xffff, 0x3148,
696	0xf292, 0xffff, 0xb865, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x930c, 0xffff, 0xffff, 0x3148,
697	0xc906, 0xffff, 0xf45d, 0x249f, 0x0000, 0x0000, 0x0000, 0x0000, 0x5ea0, 0xfcf1, 0xffff, 0xffff, 0x3888,
698	0x6d81, 0xffff, 0xffff, 0xeaf5, 0x7dcf, 0x5533, 0x6c2b, 0xc07b, 0xfff1, 0xffff, 0xffff, 0xffff, 0x4a9d,
699	0x00d4, 0xa6a1, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xd54d, 0x8399, 0xffff, 0xffff, 0x764b,
700	0x0000, 0x001b, 0x4ffc, 0xbb4a, 0xe3f5, 0xeee3, 0xbd4c, 0x7e42, 0x0900, 0x1b0c, 0xb6fc, 0xb6fc, 0x7e04,
701}
702
703// TestFixedFloatingCloseness compares the closeness of the fixed point and
704// floating point rasterizer.
705func TestFixedFloatingCloseness(t *testing.T) {
706	if len(fxMask16) != len(flMask16) {
707		t.Fatalf("len(fxMask16) != len(flMask16)")
708	}
709
710	total := uint32(0)
711	for i := range fxMask16 {
712		a := fxMask16[i]
713		b := flMask16[i]
714		if a > b {
715			total += a - b
716		} else {
717			total += b - a
718		}
719	}
720	n := len(fxMask16)
721
722	// This log message is useful when changing the fixed point rasterizer
723	// implementation, such as by changing ϕ. Assuming that the floating point
724	// rasterizer is accurate, the average difference is a measure of how
725	// inaccurate the (faster) fixed point rasterizer is.
726	//
727	// Smaller is better.
728	percent := float64(total*100) / float64(n*65535)
729	t.Logf("Comparing closeness of the fixed point and floating point rasterizer.\n"+
730		"Specifically, the elements of fxMask16 and flMask16.\n"+
731		"Total diff = %d, n = %d, avg = %.5f out of 65535, or %.5f%%.\n",
732		total, n, float64(total)/float64(n), percent)
733
734	const thresholdPercent = 1.0
735	if percent > thresholdPercent {
736		t.Errorf("average difference: got %.5f%%, want <= %.5f%%", percent, thresholdPercent)
737	}
738}
739