COLRVariationMerger: further optimize DeltaSetIndexMap
previously we only reused the VarIndexBase of a previously seen variable table when the current's varIdxes were _fully_ equal to one of the previous; now we also try to find a match anywhere in the accummulated list of self.varIdxes, including a partial match at the tail of the list.
This commit is contained in:
parent
c397764720
commit
d85aa2d119
@ -4,7 +4,6 @@ Merge OpenType Layout tables (GDEF / GPOS / GSUB).
|
||||
import os
|
||||
import copy
|
||||
import enum
|
||||
import itertools
|
||||
from operator import ior
|
||||
import logging
|
||||
from fontTools.misc import classifyTools
|
||||
@ -1126,8 +1125,8 @@ class COLRVariationMerger(VariationMerger):
|
||||
# maps {tuple(varIdxes): VarIndexBase} to facilitate reuse of VarIndexBase
|
||||
# between variable tables with same varIdxes.
|
||||
self.varIndexCache = {}
|
||||
# total number of varIdxes (i.e. sum(len(vs) for vs in self.varIndexCache))
|
||||
self.varIndexCount = 0
|
||||
# flat list of all the varIdxes generated while merging
|
||||
self.varIdxes = []
|
||||
# set of id()s of the subtables that contain variations after merging
|
||||
# and need to be upgraded to the associated VarType.
|
||||
self.varTableIds = set()
|
||||
@ -1135,18 +1134,6 @@ class COLRVariationMerger(VariationMerger):
|
||||
def mergeTables(self, font, master_ttfs, tableTags=("COLR",)):
|
||||
VariationMerger.mergeTables(self, font, master_ttfs, tableTags)
|
||||
|
||||
@property
|
||||
def varIdxes(self):
|
||||
"""Return flat list of all the varIdxes generated while merging.
|
||||
|
||||
To be called after mergeTables() for building a DeltaSetIndexMap.
|
||||
"""
|
||||
# dict remembers insertion order (as of py37+), and we extend the cache
|
||||
# of VarIndexBases incrementally as new, unique tuples of varIdxes are found
|
||||
# while merging, thus we don't need to sort but we just unpack the keys and
|
||||
# chain the tuples
|
||||
return [v for v in itertools.chain(*self.varIndexCache.keys())]
|
||||
|
||||
def checkFormatEnum(self, out, lst, validate=lambda _: True):
|
||||
fmt = out.Format
|
||||
formatEnum = out.formatEnum
|
||||
@ -1219,6 +1206,35 @@ class COLRVariationMerger(VariationMerger):
|
||||
|
||||
return baseValue, varIdx
|
||||
|
||||
def storeVariationIndices(self, varIdxes) -> int:
|
||||
# try to reuse an existing VarIndexBase for the same varIdxes, or else
|
||||
# create a new one
|
||||
key = tuple(varIdxes)
|
||||
varIndexBase = self.varIndexCache.get(key)
|
||||
|
||||
if varIndexBase is None:
|
||||
# scan for a full match anywhere in the self.varIdxes
|
||||
for i in range(len(self.varIdxes) - len(varIdxes) + 1):
|
||||
if self.varIdxes[i:i+len(varIdxes)] == varIdxes:
|
||||
self.varIndexCache[key] = varIndexBase = i
|
||||
break
|
||||
|
||||
if varIndexBase is None:
|
||||
# try find a partial match at the end of the self.varIdxes
|
||||
for n in range(len(varIdxes)-1, 0, -1):
|
||||
if self.varIdxes[-n:] == varIdxes[:n]:
|
||||
varIndexBase = len(self.varIdxes) - n
|
||||
self.varIndexCache[key] = varIndexBase
|
||||
self.varIdxes.extend(varIdxes[n:])
|
||||
break
|
||||
|
||||
if varIndexBase is None:
|
||||
# no match found, append at the end
|
||||
self.varIndexCache[key] = varIndexBase = len(self.varIdxes)
|
||||
self.varIdxes.extend(varIdxes)
|
||||
|
||||
return varIndexBase
|
||||
|
||||
def mergeVariableAttrs(self, out, lst, attrs) -> int:
|
||||
varIndexBase = ot.NO_VARIATION_INDEX
|
||||
varIdxes = []
|
||||
@ -1226,14 +1242,9 @@ class COLRVariationMerger(VariationMerger):
|
||||
baseValue, varIdx = self.storeMastersForAttr(out, lst, attr)
|
||||
setattr(out, attr, baseValue)
|
||||
varIdxes.append(varIdx)
|
||||
varIdxes = tuple(varIdxes)
|
||||
|
||||
if any(v != ot.NO_VARIATION_INDEX for v in varIdxes):
|
||||
# try to reuse an existing VarIndexBase for the same varIdxes, or else
|
||||
# create a new one at the end of self.varIndexCache (py37+ dicts are ordered)
|
||||
varIndexBase = self.varIndexCache.setdefault(varIdxes, self.varIndexCount)
|
||||
if varIndexBase == self.varIndexCount:
|
||||
self.varIndexCount += len(varIdxes)
|
||||
varIndexBase = self.storeVariationIndices(varIdxes)
|
||||
|
||||
return varIndexBase
|
||||
|
||||
|
@ -283,7 +283,7 @@ class COLRVariationMergerTest:
|
||||
' <y1 value="1"/>',
|
||||
' <x2 value="2"/>',
|
||||
' <y2 value="2"/>',
|
||||
' <VarIndexBase value="2"/>',
|
||||
' <VarIndexBase value="1"/>',
|
||||
"</Paint>",
|
||||
],
|
||||
[
|
||||
@ -293,7 +293,6 @@ class COLRVariationMergerTest:
|
||||
NO_VARIATION_INDEX,
|
||||
NO_VARIATION_INDEX,
|
||||
NO_VARIATION_INDEX,
|
||||
NO_VARIATION_INDEX,
|
||||
1,
|
||||
],
|
||||
id="linear_grad-stop[0].offset-y2",
|
||||
@ -475,7 +474,7 @@ class COLRVariationMergerTest:
|
||||
' <centerY value="0"/>',
|
||||
' <startAngle value="0.0"/>',
|
||||
' <endAngle value="180.0"/>',
|
||||
' <VarIndexBase/>',
|
||||
" <VarIndexBase/>",
|
||||
"</Paint>",
|
||||
],
|
||||
[NO_VARIATION_INDEX, 0],
|
||||
@ -599,6 +598,106 @@ class COLRVariationMergerTest:
|
||||
],
|
||||
id="transform-yy-dy",
|
||||
),
|
||||
pytest.param(
|
||||
[
|
||||
{
|
||||
"Format": ot.PaintFormat.PaintTransform,
|
||||
"Paint": {
|
||||
"Format": ot.PaintFormat.PaintSweepGradient,
|
||||
"ColorLine": {
|
||||
"Extend": ot.ExtendMode.PAD,
|
||||
"ColorStop": [
|
||||
{"StopOffset": 0.0, "PaletteIndex": 0},
|
||||
{
|
||||
"StopOffset": 1.0,
|
||||
"PaletteIndex": 1,
|
||||
"Alpha": 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
"centerX": 0,
|
||||
"centerY": 0,
|
||||
"startAngle": -360,
|
||||
"endAngle": 0,
|
||||
},
|
||||
"Transform": (1.0, 0, 0, 1.0, 0, 0),
|
||||
},
|
||||
{
|
||||
"Format": ot.PaintFormat.PaintTransform,
|
||||
"Paint": {
|
||||
"Format": ot.PaintFormat.PaintSweepGradient,
|
||||
"ColorLine": {
|
||||
"Extend": ot.ExtendMode.PAD,
|
||||
"ColorStop": [
|
||||
{"StopOffset": 0.0, "PaletteIndex": 0},
|
||||
{
|
||||
"StopOffset": 1.0,
|
||||
"PaletteIndex": 1,
|
||||
"Alpha": 1.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
"centerX": 256,
|
||||
"centerY": 0,
|
||||
"startAngle": -360,
|
||||
"endAngle": 0,
|
||||
},
|
||||
# Transform.xx below produces the same VarStore delta as the
|
||||
# above PaintSweepGradient's centerX because, when Fixed16.16
|
||||
# is converted to integer, it becomes:
|
||||
# floatToFixed(1.00390625, 16) == 256
|
||||
# Because there is overlap between the varIdxes of the
|
||||
# PaintVarTransform's Affine2x3 and the PaintSweepGradient's
|
||||
# the VarIndexBase is reused (0 for both)
|
||||
"Transform": (1.00390625, 0, 0, 1.0, 10, 0),
|
||||
},
|
||||
],
|
||||
[
|
||||
'<Paint Format="13"><!-- PaintVarTransform -->',
|
||||
' <Paint Format="9"><!-- PaintVarSweepGradient -->',
|
||||
" <ColorLine>",
|
||||
' <Extend value="pad"/>',
|
||||
" <!-- StopCount=2 -->",
|
||||
' <ColorStop index="0">',
|
||||
' <StopOffset value="0.0"/>',
|
||||
' <PaletteIndex value="0"/>',
|
||||
' <Alpha value="1.0"/>',
|
||||
" <VarIndexBase/>",
|
||||
" </ColorStop>",
|
||||
' <ColorStop index="1">',
|
||||
' <StopOffset value="1.0"/>',
|
||||
' <PaletteIndex value="1"/>',
|
||||
' <Alpha value="1.0"/>',
|
||||
" <VarIndexBase/>",
|
||||
" </ColorStop>",
|
||||
" </ColorLine>",
|
||||
' <centerX value="0"/>',
|
||||
' <centerY value="0"/>',
|
||||
' <startAngle value="-360.0"/>',
|
||||
' <endAngle value="0.0"/>',
|
||||
' <VarIndexBase value="0"/>',
|
||||
" </Paint>",
|
||||
" <Transform>",
|
||||
' <xx value="1.0"/>',
|
||||
' <yx value="0.0"/>',
|
||||
' <xy value="0.0"/>',
|
||||
' <yy value="1.0"/>',
|
||||
' <dx value="0.0"/>',
|
||||
' <dy value="0.0"/>',
|
||||
' <VarIndexBase value="0"/>',
|
||||
" </Transform>",
|
||||
"</Paint>",
|
||||
],
|
||||
[
|
||||
0,
|
||||
NO_VARIATION_INDEX,
|
||||
NO_VARIATION_INDEX,
|
||||
NO_VARIATION_INDEX,
|
||||
1,
|
||||
NO_VARIATION_INDEX,
|
||||
],
|
||||
id="transform-xx-sweep_grad-centerx-same-varidxbase",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_merge_Paint(self, paints, ttFont, expected_xml, expected_varIdxes):
|
||||
|
Loading…
x
Reference in New Issue
Block a user