multiVarStore.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. from fontTools.misc.roundTools import noRound, otRound
  2. from fontTools.misc.intTools import bit_count
  3. from fontTools.misc.vector import Vector
  4. from fontTools.ttLib.tables import otTables as ot
  5. from fontTools.varLib.models import supportScalar
  6. import fontTools.varLib.varStore # For monkey-patching
  7. from fontTools.varLib.builder import (
  8. buildVarRegionList,
  9. buildSparseVarRegionList,
  10. buildSparseVarRegion,
  11. buildMultiVarStore,
  12. buildMultiVarData,
  13. )
  14. from fontTools.misc.iterTools import batched
  15. from functools import partial
  16. from collections import defaultdict
  17. from heapq import heappush, heappop
  18. NO_VARIATION_INDEX = ot.NO_VARIATION_INDEX
  19. ot.MultiVarStore.NO_VARIATION_INDEX = NO_VARIATION_INDEX
  20. def _getLocationKey(loc):
  21. return tuple(sorted(loc.items(), key=lambda kv: kv[0]))
  22. class OnlineMultiVarStoreBuilder(object):
  23. def __init__(self, axisTags):
  24. self._axisTags = axisTags
  25. self._regionMap = {}
  26. self._regionList = buildSparseVarRegionList([], axisTags)
  27. self._store = buildMultiVarStore(self._regionList, [])
  28. self._data = None
  29. self._model = None
  30. self._supports = None
  31. self._varDataIndices = {}
  32. self._varDataCaches = {}
  33. self._cache = None
  34. def setModel(self, model):
  35. self.setSupports(model.supports)
  36. self._model = model
  37. def setSupports(self, supports):
  38. self._model = None
  39. self._supports = list(supports)
  40. if not self._supports[0]:
  41. del self._supports[0] # Drop base master support
  42. self._cache = None
  43. self._data = None
  44. def finish(self):
  45. self._regionList.RegionCount = len(self._regionList.Region)
  46. self._store.MultiVarDataCount = len(self._store.MultiVarData)
  47. return self._store
  48. def _add_MultiVarData(self):
  49. regionMap = self._regionMap
  50. regionList = self._regionList
  51. regions = self._supports
  52. regionIndices = []
  53. for region in regions:
  54. key = _getLocationKey(region)
  55. idx = regionMap.get(key)
  56. if idx is None:
  57. varRegion = buildSparseVarRegion(region, self._axisTags)
  58. idx = regionMap[key] = len(regionList.Region)
  59. regionList.Region.append(varRegion)
  60. regionIndices.append(idx)
  61. # Check if we have one already...
  62. key = tuple(regionIndices)
  63. varDataIdx = self._varDataIndices.get(key)
  64. if varDataIdx is not None:
  65. self._outer = varDataIdx
  66. self._data = self._store.MultiVarData[varDataIdx]
  67. self._cache = self._varDataCaches[key]
  68. if len(self._data.Item) == 0xFFFF:
  69. # This is full. Need new one.
  70. varDataIdx = None
  71. if varDataIdx is None:
  72. self._data = buildMultiVarData(regionIndices, [])
  73. self._outer = len(self._store.MultiVarData)
  74. self._store.MultiVarData.append(self._data)
  75. self._varDataIndices[key] = self._outer
  76. if key not in self._varDataCaches:
  77. self._varDataCaches[key] = {}
  78. self._cache = self._varDataCaches[key]
  79. def storeMasters(self, master_values, *, round=round):
  80. deltas = self._model.getDeltas(master_values, round=round)
  81. base = deltas.pop(0)
  82. return base, self.storeDeltas(deltas, round=noRound)
  83. def storeDeltas(self, deltas, *, round=round):
  84. deltas = tuple(round(d) for d in deltas)
  85. if not any(deltas):
  86. return NO_VARIATION_INDEX
  87. deltas_tuple = tuple(tuple(d) for d in deltas)
  88. if not self._data:
  89. self._add_MultiVarData()
  90. varIdx = self._cache.get(deltas_tuple)
  91. if varIdx is not None:
  92. return varIdx
  93. inner = len(self._data.Item)
  94. if inner == 0xFFFF:
  95. # Full array. Start new one.
  96. self._add_MultiVarData()
  97. return self.storeDeltas(deltas, round=noRound)
  98. self._data.addItem(deltas, round=noRound)
  99. varIdx = (self._outer << 16) + inner
  100. self._cache[deltas_tuple] = varIdx
  101. return varIdx
  102. def MultiVarData_addItem(self, deltas, *, round=round):
  103. deltas = tuple(round(d) for d in deltas)
  104. assert len(deltas) == self.VarRegionCount
  105. values = []
  106. for d in deltas:
  107. values.extend(d)
  108. self.Item.append(values)
  109. self.ItemCount = len(self.Item)
  110. ot.MultiVarData.addItem = MultiVarData_addItem
  111. def SparseVarRegion_get_support(self, fvar_axes):
  112. return {
  113. fvar_axes[reg.AxisIndex].axisTag: (reg.StartCoord, reg.PeakCoord, reg.EndCoord)
  114. for reg in self.SparseVarRegionAxis
  115. }
  116. ot.SparseVarRegion.get_support = SparseVarRegion_get_support
  117. def MultiVarStore___bool__(self):
  118. return bool(self.MultiVarData)
  119. ot.MultiVarStore.__bool__ = MultiVarStore___bool__
  120. class MultiVarStoreInstancer(object):
  121. def __init__(self, multivarstore, fvar_axes, location={}):
  122. self.fvar_axes = fvar_axes
  123. assert multivarstore is None or multivarstore.Format == 1
  124. self._varData = multivarstore.MultiVarData if multivarstore else []
  125. self._regions = (
  126. multivarstore.SparseVarRegionList.Region if multivarstore else []
  127. )
  128. self.setLocation(location)
  129. def setLocation(self, location):
  130. self.location = dict(location)
  131. self._clearCaches()
  132. def _clearCaches(self):
  133. self._scalars = {}
  134. def _getScalar(self, regionIdx):
  135. scalar = self._scalars.get(regionIdx)
  136. if scalar is None:
  137. support = self._regions[regionIdx].get_support(self.fvar_axes)
  138. scalar = supportScalar(self.location, support)
  139. self._scalars[regionIdx] = scalar
  140. return scalar
  141. @staticmethod
  142. def interpolateFromDeltasAndScalars(deltas, scalars):
  143. if not deltas:
  144. return Vector([])
  145. assert len(deltas) % len(scalars) == 0, (len(deltas), len(scalars))
  146. m = len(deltas) // len(scalars)
  147. delta = Vector([0] * m)
  148. for d, s in zip(batched(deltas, m), scalars):
  149. if not s:
  150. continue
  151. delta += Vector(d) * s
  152. return delta
  153. def __getitem__(self, varidx):
  154. major, minor = varidx >> 16, varidx & 0xFFFF
  155. if varidx == NO_VARIATION_INDEX:
  156. return Vector([])
  157. varData = self._varData
  158. scalars = [self._getScalar(ri) for ri in varData[major].VarRegionIndex]
  159. deltas = varData[major].Item[minor]
  160. return self.interpolateFromDeltasAndScalars(deltas, scalars)
  161. def interpolateFromDeltas(self, varDataIndex, deltas):
  162. varData = self._varData
  163. scalars = [self._getScalar(ri) for ri in varData[varDataIndex].VarRegionIndex]
  164. return self.interpolateFromDeltasAndScalars(deltas, scalars)
  165. def MultiVarStore_subset_varidxes(self, varIdxes):
  166. return ot.VarStore.subset_varidxes(self, varIdxes, VarData="MultiVarData")
  167. def MultiVarStore_prune_regions(self):
  168. return ot.VarStore.prune_regions(
  169. self, VarData="MultiVarData", VarRegionList="SparseVarRegionList"
  170. )
  171. ot.MultiVarStore.prune_regions = MultiVarStore_prune_regions
  172. ot.MultiVarStore.subset_varidxes = MultiVarStore_subset_varidxes
  173. def MultiVarStore_get_supports(self, major, fvarAxes):
  174. supports = []
  175. varData = self.MultiVarData[major]
  176. for regionIdx in varData.VarRegionIndex:
  177. region = self.SparseVarRegionList.Region[regionIdx]
  178. support = region.get_support(fvarAxes)
  179. supports.append(support)
  180. return supports
  181. ot.MultiVarStore.get_supports = MultiVarStore_get_supports
  182. def VARC_collect_varidxes(self, varidxes):
  183. for glyph in self.VarCompositeGlyphs.VarCompositeGlyph:
  184. for component in glyph.components:
  185. varidxes.add(component.axisValuesVarIndex)
  186. varidxes.add(component.transformVarIndex)
  187. def VARC_remap_varidxes(self, varidxes_map):
  188. for glyph in self.VarCompositeGlyphs.VarCompositeGlyph:
  189. for component in glyph.components:
  190. component.axisValuesVarIndex = varidxes_map[component.axisValuesVarIndex]
  191. component.transformVarIndex = varidxes_map[component.transformVarIndex]
  192. ot.VARC.collect_varidxes = VARC_collect_varidxes
  193. ot.VARC.remap_varidxes = VARC_remap_varidxes