colorLib: factor out LayerReuseCache class
This commit is contained in:
parent
c2887caf95
commit
eb00e499c0
@ -449,30 +449,15 @@ def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]:
|
|||||||
yield (lbound, ubound)
|
yield (lbound, ubound)
|
||||||
|
|
||||||
|
|
||||||
class LayerListBuilder:
|
class LayerReuseCache:
|
||||||
layers: List[ot.Paint]
|
|
||||||
reusePool: Mapping[Tuple[Any, ...], int]
|
reusePool: Mapping[Tuple[Any, ...], int]
|
||||||
tuples: Mapping[int, Tuple[Any, ...]]
|
tuples: Mapping[int, Tuple[Any, ...]]
|
||||||
keepAlive: List[ot.Paint] # we need id to remain valid
|
keepAlive: List[ot.Paint] # we need id to remain valid
|
||||||
allowLayerReuse: bool
|
|
||||||
|
|
||||||
def __init__(self, *, allowLayerReuse=True):
|
def __init__(self):
|
||||||
self.layers = []
|
|
||||||
self.reusePool = {}
|
self.reusePool = {}
|
||||||
self.tuples = {}
|
self.tuples = {}
|
||||||
self.keepAlive = []
|
self.keepAlive = []
|
||||||
self.allowLayerReuse = allowLayerReuse
|
|
||||||
|
|
||||||
# We need to intercept construction of PaintColrLayers
|
|
||||||
callbacks = _buildPaintCallbacks()
|
|
||||||
callbacks[
|
|
||||||
(
|
|
||||||
BuildCallback.BEFORE_BUILD,
|
|
||||||
ot.Paint,
|
|
||||||
ot.PaintFormat.PaintColrLayers,
|
|
||||||
)
|
|
||||||
] = self._beforeBuildPaintColrLayers
|
|
||||||
self.tableBuilder = TableBuilder(callbacks)
|
|
||||||
|
|
||||||
def _paint_tuple(self, paint: ot.Paint):
|
def _paint_tuple(self, paint: ot.Paint):
|
||||||
# start simple, who even cares about cyclic graphs or interesting field types
|
# start simple, who even cares about cyclic graphs or interesting field types
|
||||||
@ -499,6 +484,61 @@ class LayerListBuilder:
|
|||||||
def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]:
|
def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]:
|
||||||
return tuple(self._paint_tuple(p) for p in paints)
|
return tuple(self._paint_tuple(p) for p in paints)
|
||||||
|
|
||||||
|
def try_reuse(self, layers: List[ot.Paint]) -> List[ot.Paint]:
|
||||||
|
found_reuse = True
|
||||||
|
while found_reuse:
|
||||||
|
found_reuse = False
|
||||||
|
|
||||||
|
ranges = sorted(
|
||||||
|
_reuse_ranges(len(layers)),
|
||||||
|
key=lambda t: (t[1] - t[0], t[1], t[0]),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
for lbound, ubound in ranges:
|
||||||
|
reuse_lbound = self.reusePool.get(
|
||||||
|
self._as_tuple(layers[lbound:ubound]), -1
|
||||||
|
)
|
||||||
|
if reuse_lbound == -1:
|
||||||
|
continue
|
||||||
|
new_slice = ot.Paint()
|
||||||
|
new_slice.Format = int(ot.PaintFormat.PaintColrLayers)
|
||||||
|
new_slice.NumLayers = ubound - lbound
|
||||||
|
new_slice.FirstLayerIndex = reuse_lbound
|
||||||
|
layers = layers[:lbound] + [new_slice] + layers[ubound:]
|
||||||
|
found_reuse = True
|
||||||
|
break
|
||||||
|
return layers
|
||||||
|
|
||||||
|
def add(self, layers: List[ot.Paint], first_layer_index: int):
|
||||||
|
for lbound, ubound in _reuse_ranges(len(layers)):
|
||||||
|
self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
|
||||||
|
lbound + first_layer_index
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LayerListBuilder:
|
||||||
|
layers: List[ot.Paint]
|
||||||
|
cache: LayerReuseCache
|
||||||
|
allowLayerReuse: bool
|
||||||
|
|
||||||
|
def __init__(self, *, allowLayerReuse=True):
|
||||||
|
self.layers = []
|
||||||
|
if allowLayerReuse:
|
||||||
|
self.cache = LayerReuseCache()
|
||||||
|
else:
|
||||||
|
self.cache = None
|
||||||
|
|
||||||
|
# We need to intercept construction of PaintColrLayers
|
||||||
|
callbacks = _buildPaintCallbacks()
|
||||||
|
callbacks[
|
||||||
|
(
|
||||||
|
BuildCallback.BEFORE_BUILD,
|
||||||
|
ot.Paint,
|
||||||
|
ot.PaintFormat.PaintColrLayers,
|
||||||
|
)
|
||||||
|
] = self._beforeBuildPaintColrLayers
|
||||||
|
self.tableBuilder = TableBuilder(callbacks)
|
||||||
|
|
||||||
# COLR layers is unusual in that it modifies shared state
|
# COLR layers is unusual in that it modifies shared state
|
||||||
# so we need a callback into an object
|
# so we need a callback into an object
|
||||||
def _beforeBuildPaintColrLayers(self, dest, source):
|
def _beforeBuildPaintColrLayers(self, dest, source):
|
||||||
@ -512,35 +552,14 @@ class LayerListBuilder:
|
|||||||
# Convert maps seqs or whatever into typed objects
|
# Convert maps seqs or whatever into typed objects
|
||||||
layers = [self.buildPaint(l) for l in layers]
|
layers = [self.buildPaint(l) for l in layers]
|
||||||
|
|
||||||
if self.allowLayerReuse:
|
# No reason to have a colr layers with just one entry
|
||||||
# No reason to have a colr layers with just one entry
|
if len(layers) == 1:
|
||||||
if len(layers) == 1:
|
return layers[0], {}
|
||||||
return layers[0], {}
|
|
||||||
|
|
||||||
|
if self.cache is not None:
|
||||||
# Look for reuse, with preference to longer sequences
|
# Look for reuse, with preference to longer sequences
|
||||||
# This may make the layer list smaller
|
# This may make the layer list smaller
|
||||||
found_reuse = True
|
layers = self.cache.try_reuse(layers)
|
||||||
while found_reuse:
|
|
||||||
found_reuse = False
|
|
||||||
|
|
||||||
ranges = sorted(
|
|
||||||
_reuse_ranges(len(layers)),
|
|
||||||
key=lambda t: (t[1] - t[0], t[1], t[0]),
|
|
||||||
reverse=True,
|
|
||||||
)
|
|
||||||
for lbound, ubound in ranges:
|
|
||||||
reuse_lbound = self.reusePool.get(
|
|
||||||
self._as_tuple(layers[lbound:ubound]), -1
|
|
||||||
)
|
|
||||||
if reuse_lbound == -1:
|
|
||||||
continue
|
|
||||||
new_slice = ot.Paint()
|
|
||||||
new_slice.Format = int(ot.PaintFormat.PaintColrLayers)
|
|
||||||
new_slice.NumLayers = ubound - lbound
|
|
||||||
new_slice.FirstLayerIndex = reuse_lbound
|
|
||||||
layers = layers[:lbound] + [new_slice] + layers[ubound:]
|
|
||||||
found_reuse = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# The layer list is now final; if it's too big we need to tree it
|
# The layer list is now final; if it's too big we need to tree it
|
||||||
is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT
|
is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT
|
||||||
@ -561,7 +580,7 @@ class LayerListBuilder:
|
|||||||
layers = [listToColrLayers(l) for l in layers]
|
layers = [listToColrLayers(l) for l in layers]
|
||||||
|
|
||||||
# No reason to have a colr layers with just one entry
|
# No reason to have a colr layers with just one entry
|
||||||
if self.allowLayerReuse and len(layers) == 1:
|
if len(layers) == 1:
|
||||||
return layers[0], {}
|
return layers[0], {}
|
||||||
|
|
||||||
paint = ot.Paint()
|
paint = ot.Paint()
|
||||||
@ -572,11 +591,8 @@ class LayerListBuilder:
|
|||||||
|
|
||||||
# Register our parts for reuse provided we aren't a tree
|
# Register our parts for reuse provided we aren't a tree
|
||||||
# If we are a tree the leaves registered for reuse and that will suffice
|
# If we are a tree the leaves registered for reuse and that will suffice
|
||||||
if self.allowLayerReuse and not is_tree:
|
if self.cache is not None and not is_tree:
|
||||||
for lbound, ubound in _reuse_ranges(len(layers)):
|
self.cache.add(layers, paint.FirstLayerIndex)
|
||||||
self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
|
|
||||||
lbound + paint.FirstLayerIndex
|
|
||||||
)
|
|
||||||
|
|
||||||
# we've fully built dest; empty source prevents generalized build from kicking in
|
# we've fully built dest; empty source prevents generalized build from kicking in
|
||||||
return paint, {}
|
return paint, {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user