From c91984ef7727906061708345817ce8c2967169a7 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 18 Dec 2023 13:15:38 -0700 Subject: [PATCH] [VARC] Use sparse-regions in MultiVarStore Might revert as the savings are small. https://github.com/harfbuzz/boring-expansion-spec/issues/103#issuecomment-1861531669 --- Lib/fontTools/ttLib/tables/otData.py | 25 +- Lib/fontTools/varLib/builder.py | 37 ++- Lib/fontTools/varLib/instancer/__init__.py | 25 +- Lib/fontTools/varLib/multiVarStore.py | 29 ++- Lib/fontTools/varLib/varStore.py | 6 +- Tests/ttLib/data/varc-6868.ttf | Bin 20296 -> 9852 bytes Tests/ttLib/data/varc-ac00-ac01-500upem.ttx | 263 ++------------------ Tests/ttLib/data/varc-ac00-ac01.ttf | Bin 3552 -> 3292 bytes 8 files changed, 115 insertions(+), 270 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 9c9efc595..6e34945bf 100644 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -3323,6 +3323,29 @@ otData = [ ], ), # MultiVariationStore + ( + "SparseVarRegionAxis", + [ + ("uint16", "AxisIndex", None, None, ""), + ("F2Dot14", "StartCoord", None, None, ""), + ("F2Dot14", "PeakCoord", None, None, ""), + ("F2Dot14", "EndCoord", None, None, ""), + ], + ), + ( + "SparseVarRegion", + [ + ("uint16", "SparseRegionCount", None, None, ""), + ("struct", "SparseVarRegionAxis", "SparseRegionCount", 0, ""), + ], + ), + ( + "SparseVarRegionList", + [ + ("uint16", "RegionCount", None, None, ""), + ("LOffsetTo(SparseVarRegion)", "Region", "RegionCount", 0, ""), + ], + ), ( "MultiVarData", [ @@ -3336,7 +3359,7 @@ otData = [ "MultiVarStore", [ ("uint16", "Format", None, None, "Set to 1."), - ("LOffset", "VarRegionList", None, None, ""), + ("LOffset", "SparseVarRegionList", None, None, ""), ("uint16", "MultiVarDataCount", None, None, ""), ("LOffset", "MultiVarData", "MultiVarDataCount", 0, ""), ], diff --git a/Lib/fontTools/varLib/builder.py b/Lib/fontTools/varLib/builder.py index f05802770..456c34c4d 100644 --- a/Lib/fontTools/varLib/builder.py +++ b/Lib/fontTools/varLib/builder.py @@ -10,6 +10,13 @@ def buildVarRegionAxis(axisSupport): return self +def buildSparseVarRegionAxis(axisIndex, axisSupport): + self = ot.SparseVarRegionAxis() + self.AxisIndex = axisIndex + self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport] + return self + + def buildVarRegion(support, axisTags): assert all(tag in axisTags for tag in support.keys()), ( "Unknown axis tag found.", @@ -23,6 +30,24 @@ def buildVarRegion(support, axisTags): return self +def buildSparseVarRegion(support, axisTags): + assert all(tag in axisTags for tag in support.keys()), ( + "Unknown axis tag found.", + support, + axisTags, + ) + self = ot.SparseVarRegion() + self.SparseVarRegionAxis = [] + for i, tag in enumerate(axisTags): + if tag not in support: + continue + self.SparseVarRegionAxis.append( + buildSparseVarRegionAxis(i, support.get(tag, (0, 0, 0))) + ) + self.SparseRegionCount = len(self.SparseVarRegionAxis) + return self + + def buildVarRegionList(supports, axisTags): self = ot.VarRegionList() self.RegionAxisCount = len(axisTags) @@ -33,6 +58,16 @@ def buildVarRegionList(supports, axisTags): return self +def buildSparseVarRegionList(supports, axisTags): + self = ot.SparseVarRegionList() + self.RegionAxisCount = len(axisTags) + self.Region = [] + for support in supports: + self.Region.append(buildSparseVarRegion(support, axisTags)) + self.RegionCount = len(self.Region) + return self + + def _reorderItem(lst, mapping): return [lst[i] for i in mapping] @@ -147,7 +182,7 @@ def buildMultiVarData(varRegionIndices, items): def buildMultiVarStore(varRegionList, multiVarDataList): self = ot.MultiVarStore() self.Format = 1 - self.VarRegionList = varRegionList + self.SparseVarRegionList = varRegionList self.MultiVarData = list(multiVarDataList) self.MultiVarDataCount = len(self.MultiVarData) return self diff --git a/Lib/fontTools/varLib/instancer/__init__.py b/Lib/fontTools/varLib/instancer/__init__.py index 69977d257..b3e27bd2c 100644 --- a/Lib/fontTools/varLib/instancer/__init__.py +++ b/Lib/fontTools/varLib/instancer/__init__.py @@ -469,7 +469,9 @@ class OverlapMode(IntEnum): def instantiateVARC(varfont, axisLimits): log.info("Instantiating VARC tables") - # TODO(behdad) My confidence in this function is rather low + # TODO(behdad) My confidence in this function is rather low; + # It needs more testing. Specially with partial-instancing, + # I don't think it currently works. varc = varfont["VARC"].table if varc.VarCompositeGlyphs: @@ -495,29 +497,28 @@ def instantiateVARC(varfont, axisLimits): fvar = varfont["fvar"] location = axisLimits.pinnedLocation() - for region in store.VarRegionList.Region: - assert len(region.VarRegionAxis) == len(fvar.axes) + for region in store.SparseVarRegionList.Region: newRegionAxis = [] - for regionTriple, fvarAxis in zip(region.VarRegionAxis, fvar.axes): - tag = fvarAxis.axisTag + for regionRecord in region.SparseVarRegionAxis: + tag = fvar.axes[regionRecord.AxisIndex].axisTag if tag in location: continue if tag in axisLimits: limits = axisLimits[tag] triple = ( - regionTriple.StartCoord, - regionTriple.PeakCoord, - regionTriple.EndCoord, + regionRecord.StartCoord, + regionRecord.PeakCoord, + regionRecord.EndCoord, ) triple = tuple( limits.renormalizeValue(v, extrapolate=False) for v in triple ) ( - regionTriple.StartCoord, - regionTriple.PeakCoord, - regionTriple.EndCoord, + regionRecord.StartCoord, + regionRecord.PeakCoord, + regionRecord.EndCoord, ) = triple - newRegionAxis.append(regionTriple) + newRegionAxis.append(regionRecord) region.VarRegionAxis = newRegionAxis region.VarRegionAxisCount = len(newRegionAxis) diff --git a/Lib/fontTools/varLib/multiVarStore.py b/Lib/fontTools/varLib/multiVarStore.py index 3d7ba71ae..6c755118b 100644 --- a/Lib/fontTools/varLib/multiVarStore.py +++ b/Lib/fontTools/varLib/multiVarStore.py @@ -6,7 +6,8 @@ from fontTools.varLib.models import supportScalar import fontTools.varLib.varStore # For monkey-patching from fontTools.varLib.builder import ( buildVarRegionList, - buildVarRegion, + buildSparseVarRegionList, + buildSparseVarRegion, buildMultiVarStore, buildMultiVarData, ) @@ -28,7 +29,7 @@ class OnlineMultiVarStoreBuilder(object): def __init__(self, axisTags): self._axisTags = axisTags self._regionMap = {} - self._regionList = buildVarRegionList([], axisTags) + self._regionList = buildSparseVarRegionList([], axisTags) self._store = buildMultiVarStore(self._regionList, []) self._data = None self._model = None @@ -64,7 +65,7 @@ class OnlineMultiVarStoreBuilder(object): key = _getLocationKey(region) idx = regionMap.get(key) if idx is None: - varRegion = buildVarRegion(region, self._axisTags) + varRegion = buildSparseVarRegion(region, self._axisTags) idx = regionMap[key] = len(regionList.Region) regionList.Region.append(varRegion) regionIndices.append(idx) @@ -133,6 +134,16 @@ def MultiVarData_addItem(self, deltas, *, round=round): ot.MultiVarData.addItem = MultiVarData_addItem +def SparseVarRegion_get_support(self, fvar_axes): + return { + fvar_axes[reg.AxisIndex].axisTag: (reg.StartCoord, reg.PeakCoord, reg.EndCoord) + for reg in self.SparseVarRegionAxis + } + + +ot.SparseVarRegion.get_support = SparseVarRegion_get_support + + def MultiVarStore___bool__(self): return bool(self.MultiVarData) @@ -145,7 +156,9 @@ class MultiVarStoreInstancer(object): self.fvar_axes = fvar_axes assert multivarstore is None or multivarstore.Format == 1 self._varData = multivarstore.MultiVarData if multivarstore else [] - self._regions = multivarstore.VarRegionList.Region if multivarstore else [] + self._regions = ( + multivarstore.SparseVarRegionList.Region if multivarstore else [] + ) self.setLocation(location) def setLocation(self, location): @@ -195,8 +208,10 @@ def MultiVarStore_subset_varidxes(self, varIdxes): return ot.VarStore.subset_varidxes(self, varIdxes, VarData="MultiVarData") -def MultiVarStore_prune_regions(self, *, VarData="VarData"): - return ot.VarStore.prune_regions(self, VarData="MultiVarData") +def MultiVarStore_prune_regions(self): + return ot.VarStore.prune_regions( + self, VarData="MultiVarData", VarRegionList="SparseVarRegionList" + ) ot.MultiVarStore.prune_regions = MultiVarStore_prune_regions @@ -207,7 +222,7 @@ def MultiVarStore_get_supports(self, major, fvarAxes): supports = [] varData = self.MultiVarData[major] for regionIdx in varData.VarRegionIndex: - region = self.VarRegionList.Region[regionIdx] + region = self.SparseVarRegionList.Region[regionIdx] support = region.get_support(fvarAxes) supports.append(support) return supports diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index ecfc02fc7..f54fad2db 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -305,7 +305,7 @@ def VarStore_subset_varidxes( setattr(self, VarData, newVarData) setattr(self, VarData + "Count", len(newVarData)) - self.prune_regions(VarData=VarData) + self.prune_regions() return varDataMap @@ -313,7 +313,7 @@ def VarStore_subset_varidxes( ot.VarStore.subset_varidxes = VarStore_subset_varidxes -def VarStore_prune_regions(self, *, VarData="VarData"): +def VarStore_prune_regions(self, *, VarData="VarData", VarRegionList="VarRegionList"): """Remove unused VarRegions.""" # # Subset VarRegionList @@ -324,7 +324,7 @@ def VarStore_prune_regions(self, *, VarData="VarData"): for data in getattr(self, VarData): usedRegions.update(data.VarRegionIndex) # Subset. - regionList = self.VarRegionList + regionList = getattr(self, VarRegionList) regions = regionList.Region newRegions = [] regionMap = {} diff --git a/Tests/ttLib/data/varc-6868.ttf b/Tests/ttLib/data/varc-6868.ttf index 66cf83e30d3a31822ebc03dd304b869ca1c61e0c..71d94e04244b1661728d69e8139c10978cea12df 100644 GIT binary patch delta 648 zcmX|;JuE{}6oAireJ!uAs;DX+gN0EfmPWWDDTBn%AR;6Nvx&s^#N0)i7|Yn$s98FY zGzNpoU^0k^!DNsQzP{I<+16HlG(ss5wUkgVm%^`Et06pB!wzagoaoxmK<8|9B zxJcW2u^=jw&_quyqlsya^=YhMZ9t8WFj2ihwVc|J8t-VLyx|4a5w%e@Uu{f{pEm8q R9ILiBTXHR_ea`P^{sEKMNX`HN delta 790 zcmez4b7CH&3 - - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + diff --git a/Tests/ttLib/data/varc-ac00-ac01.ttf b/Tests/ttLib/data/varc-ac00-ac01.ttf index a8cbf6f7415c8d267e03749a9fcbcd4468054277..cfe10a044f2552e06cac8c125266d6f11c43e369 100644 GIT binary patch delta 122 zcmaDLeMeG;fsuiMfrp`iftkS}%rVG$d)?Hz3=FIR3=E786J@NJ*d}a@FkxYdGj_Z; zc>>EhCWaN06Iew|861Eb7#R3~SOSQZz{UUt9f0~kY%q%nC<7A%(UVuRz1TdJ-J1;n D%}o`z delta 157 zcmca3`9NBRfsuiMfrp`iftkS}%rVG$>Vcig3=FIR3=B+86J@NJ*r#laFkxX?xbDZ9 z$rD)4F)^;1oWLr=$>_u&2-L#hFxin)X7UF%0Tf)o!q13Rh6`w!0}$fUxRUh+Bg5ti HY(8uNAN4Hy