diff --git a/font/cmap_cache.go b/font/cmap_cache.go index 02444cb..c80b700 100644 --- a/font/cmap_cache.go +++ b/font/cmap_cache.go @@ -2,41 +2,32 @@ package font // Code generated by typesetting-utils/generators/cache/gen.go. DO NOT EDIT. -/* Implements caches for integers key->value functions. - * - * The cache is a fixed-size array of 8-bit, 16-bit or 32-bit integers, - * typically 256 elements. - * - * The key is split into two parts: the cache index (high bits) - * and the rest (low bits). - * - * The memory layout is the following : - * KEY = - * VALUE = - * with the constraints - * KEY in [0, 2^key bits[ - * VALUE in [0, 2^value bits[ - * - * The cache index is used to index into the array. The array - * member is an integer that is used BOTH - * to store the low bits of the key, and the value. - * - * The value is stored in the least significant bits of the integer. - * The low bits of the key are stored in the most significant bits - * of the integer. - * - * A cache hit is detected by comparing the low bits of the key - * with the high bits of the integer at the array position indexed - * by the high bits of the key. If they match, the value is extracted - * from the least significant bits of the integer and returned. - * Otherwise, a cache miss is reported. - * - * Cache operations (storage and retrieval) involve just a few - * arithmetic operations and a single memory access. - */ - -// cache21_19_8 is a cache for integer (key, value) pairs, -// with 0 <= key < 2097152 and 0 <= value < 524288 +// cache21_19_8 implements a cache for integer pairs, +// mapping a key in [0,1 << 21[ to a value in [0,1 << 19[ +// +// The memory layout is the following : +// +// KEY : <13 bits><8 bits> +// VALUE : <13 bits><19 bits> +// +// The cache index is used to index into the array. The array +// member is an integer that is used BOTH +// to store the high bits of the key, and the value. +// The value is stored in the least significant bits of the integer. +// The high bits of the key are stored in the most significant bits +// of the integer. +// +// A cache hit is detected by comparing the high bits of the key +// with the high bits of the integer at the array position indexed +// by the low bits of the key. If they match, the value is extracted +// from the least significant bits of the integer and returned. +// Otherwise, a cache miss is reported. +// +// Cache operations (storage and retrieval) involve just a few +// arithmetic operations and a single memory access. +// +// This cache works best with sparse keys (sharing the same high 13 bits), +// since it handles 1 << 8 contiguous keys without collision. type cache21_19_8 [1 << 8]uint32 // clear should be used as init function @@ -46,7 +37,7 @@ func (c *cache21_19_8) clear() { } } -func (c cache21_19_8) get(key uint32) (uint32, bool) { +func (c *cache21_19_8) get(key uint32) (uint32, bool) { k := key & ((1 << 8) - 1) v := c[k] if v == ^uint32(0) || (v>>19) != uint32(key>>8) { @@ -68,3 +59,58 @@ func (c *cache21_19_8) setUnchecked(key uint32, value uint32) { v := (uint32(key>>8) << 19) | value c[k] = v } + +// cache21_0_13 implements a cache for integer pairs, +// mapping a key in [0,1 << 21[ to a value in [0,1 << 0[ +// +// The memory layout is the following : +// +// KEY : <8 bits><13 bits> +// VALUE : <8 bits><0 bits> +// +// The cache index is used to index into the array. The array +// member is an integer that is used BOTH +// to store the high bits of the key, and the value. +// The value is stored in the least significant bits of the integer. +// The high bits of the key are stored in the most significant bits +// of the integer. +// +// A cache hit is detected by comparing the high bits of the key +// with the high bits of the integer at the array position indexed +// by the low bits of the key. If they match, the value is extracted +// from the least significant bits of the integer and returned. +// Otherwise, a cache miss is reported. +// +// Cache operations (storage and retrieval) involve just a few +// arithmetic operations and a single memory access. +// +// This cache works best with sparse keys (sharing the same high 8 bits), +// since it handles 1 << 13 contiguous keys without collision. +type cache21_0_13 [1 << 13]uint8 + +// clear should be used as init function +func (c *cache21_0_13) clear() { + for i := range c { + c[i] = ^uint8(0) + } +} + +func (c *cache21_0_13) get(key uint32) bool { + k := key & ((1 << 13) - 1) + v := c[k] + return v != ^uint8(0) && v == uint8(key>>13) +} + +func (c *cache21_0_13) set(key uint32) { + if (key >> 21) != 0 { /* overflows */ + return + } + c.setUnchecked(key) +} + +// assumes key < 2097152 +func (c *cache21_0_13) setUnchecked(key uint32) { + k := key & ((1 << 13) - 1) + v := uint8(key >> 13) + c[k] = v +} diff --git a/font/font.go b/font/font.go index 43fbd1e..53069ab 100644 --- a/font/font.go +++ b/font/font.go @@ -620,8 +620,9 @@ func loadGDEF(ld *ot.Loader, axisCount int, gsub, gpos []byte) (tables.GDEF, err type Face struct { *Font - extentsCache extentsCache - cmapCache cache21_19_8 + extentsCache extentsCache + cmapCache cache21_19_8 // supported runes, mapping to GID + cmapNotSupportedCache cache21_0_13 // not supported runes coords []tables.Coord xPpem, yPpem uint16 @@ -631,6 +632,7 @@ type Face struct { func NewFace(font *Font) *Face { out := &Face{Font: font, extentsCache: make(extentsCache, font.nGlyphs)} out.cmapCache.clear() + out.cmapNotSupportedCache.clear() return out } @@ -639,12 +641,17 @@ func NewFace(font *Font) *Face { // Note that it only looks into the cmap, without taking account substitutions // nor variation selectors. func (f *Face) NominalGlyph(ch rune) (GID, bool) { + if notSupported := f.cmapNotSupportedCache.get(uint32(ch)); notSupported { + return 0, false + } if g, ok := f.cmapCache.get(uint32(ch)); ok { return GID(g), ok } g, ok := f.Cmap.Lookup(ch) if ok { f.cmapCache.set(uint32(ch), uint32(g)) + } else { + f.cmapNotSupportedCache.set(uint32(ch)) } return g, ok } diff --git a/font/font_test.go b/font/font_test.go index 322a668..40d9f33 100644 --- a/font/font_test.go +++ b/font/font_test.go @@ -181,3 +181,35 @@ func TestBitmapExtents(t *testing.T) { extents, ok := face.GlyphExtents(41) tu.Assert(t, ok && extents.Width == 819.2 && extents.Height == -1433.6) } + +func BenchmarkCmap(b *testing.B) { + font := loadFont(b, "common/Roboto-BoldItalic.ttf") + face := NewFace(font) + latinText := []rune("Hi this is a test with some âccents : $£8") + chineseText := []rune("襄陽曲四首/魯中都東樓醉起作-李白 刊误") + mixedText := append(latinText, chineseText...) + + b.ResetTimer() + + b.Run("latin text", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, r := range latinText { + _, _ = face.NominalGlyph(r) + } + } + }) + b.Run("chinese text", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, r := range chineseText { + _, _ = face.NominalGlyph(r) + } + } + }) + b.Run("mixed text", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, r := range mixedText { + _, _ = face.NominalGlyph(r) + } + } + }) +} diff --git a/font/renderer_test.go b/font/renderer_test.go index b77b06f..f5d03f3 100644 --- a/font/renderer_test.go +++ b/font/renderer_test.go @@ -591,7 +591,8 @@ func TestAppleBitmapGlyph(t *testing.T) { ft, err := NewFont(fonts[0]) tu.AssertNoErr(t, err) - face := Face{Font: ft, xPpem: 94, yPpem: 94} + face := NewFace(ft) + face.SetPpem(94, 94) runes := "The quick brown fox jumps over the lazy dog" for _, r := range runes { diff --git a/harfbuzz/caches.go b/harfbuzz/caches.go index 5f12bcb..0703bfb 100644 --- a/harfbuzz/caches.go +++ b/harfbuzz/caches.go @@ -2,41 +2,32 @@ package harfbuzz // Code generated by typesetting-utils/generators/cache/gen.go. DO NOT EDIT. -/* Implements caches for integers key->value functions. - * - * The cache is a fixed-size array of 8-bit, 16-bit or 32-bit integers, - * typically 256 elements. - * - * The key is split into two parts: the cache index (high bits) - * and the rest (low bits). - * - * The memory layout is the following : - * KEY = - * VALUE = - * with the constraints - * KEY in [0, 2^key bits[ - * VALUE in [0, 2^value bits[ - * - * The cache index is used to index into the array. The array - * member is an integer that is used BOTH - * to store the low bits of the key, and the value. - * - * The value is stored in the least significant bits of the integer. - * The low bits of the key are stored in the most significant bits - * of the integer. - * - * A cache hit is detected by comparing the low bits of the key - * with the high bits of the integer at the array position indexed - * by the high bits of the key. If they match, the value is extracted - * from the least significant bits of the integer and returned. - * Otherwise, a cache miss is reported. - * - * Cache operations (storage and retrieval) involve just a few - * arithmetic operations and a single memory access. - */ - -// cache15_8_7 is a cache for integer (key, value) pairs, -// with 0 <= key < 32768 and 0 <= value < 256 +// cache15_8_7 implements a cache for integer pairs, +// mapping a key in [0,1 << 15[ to a value in [0,1 << 8[ +// +// The memory layout is the following : +// +// KEY : <8 bits><7 bits> +// VALUE : <8 bits><8 bits> +// +// The cache index is used to index into the array. The array +// member is an integer that is used BOTH +// to store the high bits of the key, and the value. +// The value is stored in the least significant bits of the integer. +// The high bits of the key are stored in the most significant bits +// of the integer. +// +// A cache hit is detected by comparing the high bits of the key +// with the high bits of the integer at the array position indexed +// by the low bits of the key. If they match, the value is extracted +// from the least significant bits of the integer and returned. +// Otherwise, a cache miss is reported. +// +// Cache operations (storage and retrieval) involve just a few +// arithmetic operations and a single memory access. +// +// This cache works best with sparse keys (sharing the same high 8 bits), +// since it handles 1 << 7 contiguous keys without collision. type cache15_8_7 [1 << 7]uint16 // clear should be used as init function @@ -46,7 +37,7 @@ func (c *cache15_8_7) clear() { } } -func (c cache15_8_7) get(key uint16) (uint16, bool) { +func (c *cache15_8_7) get(key uint16) (uint16, bool) { k := key & ((1 << 7) - 1) v := c[k] if v == ^uint16(0) || (v>>8) != uint16(key>>7) { @@ -69,8 +60,32 @@ func (c *cache15_8_7) setUnchecked(key uint16, value uint16) { c[k] = v } -// cache21_3_8 is a cache for integer (key, value) pairs, -// with 0 <= key < 2097152 and 0 <= value < 8 +// cache21_3_8 implements a cache for integer pairs, +// mapping a key in [0,1 << 21[ to a value in [0,1 << 3[ +// +// The memory layout is the following : +// +// KEY : <13 bits><8 bits> +// VALUE : <13 bits><3 bits> +// +// The cache index is used to index into the array. The array +// member is an integer that is used BOTH +// to store the high bits of the key, and the value. +// The value is stored in the least significant bits of the integer. +// The high bits of the key are stored in the most significant bits +// of the integer. +// +// A cache hit is detected by comparing the high bits of the key +// with the high bits of the integer at the array position indexed +// by the low bits of the key. If they match, the value is extracted +// from the least significant bits of the integer and returned. +// Otherwise, a cache miss is reported. +// +// Cache operations (storage and retrieval) involve just a few +// arithmetic operations and a single memory access. +// +// This cache works best with sparse keys (sharing the same high 13 bits), +// since it handles 1 << 8 contiguous keys without collision. type cache21_3_8 [1 << 8]uint16 // clear should be used as init function @@ -80,7 +95,7 @@ func (c *cache21_3_8) clear() { } } -func (c cache21_3_8) get(key uint32) (uint16, bool) { +func (c *cache21_3_8) get(key uint32) (uint16, bool) { k := key & ((1 << 8) - 1) v := c[k] if v == ^uint16(0) || (v>>3) != uint16(key>>8) {