Skip to content

Commit

Permalink
if transformed radial gradient triggers OverflowError, scale it down …
Browse files Browse the repository at this point in the history
…1/10th until it does
  • Loading branch information
anthrotype committed Mar 28, 2023
1 parent f3c5195 commit 7a73f25
Showing 1 changed file with 36 additions and 10 deletions.
46 changes: 36 additions & 10 deletions src/nanoemoji/paint.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,15 @@ def check_overflows(self) -> "PaintRadialGradient":
)
return self

def apply_uniform_transform(self, transform: Affine2D) -> "PaintRadialGradient":
sx, sy = transform.getscale()
assert almost_equal(sx, sy)
c0 = transform.map_point(self.c0)
c1 = transform.map_point(self.c1)
r0 = self.r0 * sx
r1 = self.r1 * sx
return dataclasses.replace(self, c0=c0, c1=c1, r0=r0, r1=r1)

def apply_transform(self, transform: Affine2D, check_overflows=True) -> Paint:
# if gradientUnits="objectBoundingBox" and the bbox is not square, or there's some
# gradientTransform, we may end up with a transformation that does not keep the
Expand All @@ -369,18 +378,35 @@ def apply_transform(self, transform: Affine2D, check_overflows=True) -> Paint:
# then encode any remaining non-uniform transformation as a COLRv1 transform
# that wraps the PaintRadialGradient (see further below).
uniform_transform, remaining_transform = _decompose_uniform_transform(transform)
gradient = self.apply_uniform_transform(uniform_transform)

c0 = uniform_transform.map_point(self.c0)
c1 = uniform_transform.map_point(self.c1)

sx, _ = uniform_transform.getscale()
r0 = self.r0 * sx
r1 = self.r1 * sx

# TODO handle degenerate cases, fallback to solid, w/e
gradient = dataclasses.replace(self, c0=c0, c1=c1, r0=r0, r1=r1)
if check_overflows:
gradient.check_overflows()
# If the scaled up gradient geometry doesn't fit the 16-bit integer limits,
# we try to scale it down by 1/10th until it does, while also scaling up the
# remaining_transform 10x to even things out.
try:
gradient.check_overflows()
except OverflowError as error:
onetenth = Affine2D.identity().scale(1 / 10)
tenfold = Affine2D.identity().scale(10)
# 10 attempts ought to be enough! (the last famous words)
for _ in range(10):
uniform_transform = Affine2D.compose_ltr(
(uniform_transform, onetenth)
)
remaining_transform = Affine2D.compose_ltr(
(tenfold, remaining_transform)
)
gradient = self.apply_uniform_transform(uniform_transform)
try:
gradient.check_overflows()
except OverflowError:
continue # try one more time
else:
error = None # success!
break
if error:
raise error
return transformed(remaining_transform, gradient)

def round(self, ndigits: int) -> "PaintRadialGradient":
Expand Down

0 comments on commit 7a73f25

Please sign in to comment.