1
0
mirror of synced 2025-01-31 12:13:49 +01:00

Implement ability to override the canvas size, to render animations that overflow the canvas.

This commit is contained in:
Jennifer Taylor 2022-07-26 23:25:35 +00:00
parent 9dd3772380
commit d39a660eab
2 changed files with 94 additions and 16 deletions

View File

@ -191,6 +191,10 @@ class PlacedClip(PlacedObject):
self.requested_frame: Optional[int] = None
self.visible_frame: int = -1
# Root clip resizing, which we don't really support.
self.__width = 0
self.__height = 0
@property
def source(self) -> RegisteredClip:
return self.__source
@ -299,6 +303,30 @@ class PlacedClip(PlacedObject):
def _visible(self, val: Any) -> None:
self.visible = val != 0
@property
def _width(self) -> int:
calculated_width = self.__width
for obj in self.placed_objects:
if isinstance(obj, PlacedClip):
calculated_width = max(calculated_width, obj._width)
return calculated_width
@_width.setter
def _width(self, val: Any) -> None:
self.__width = val
@property
def _height(self) -> int:
calculated_height = self.__height
for obj in self.placed_objects:
if isinstance(obj, PlacedClip):
calculated_height = max(calculated_height, obj._height)
return calculated_height
@_height.setter
def _height(self, val: Any) -> None:
self.__height = val
class PlacedImage(PlacedObject):
# An image that occupies its parent clip at some depth. Placed by an AP2PlaceObjectTag
@ -612,6 +640,8 @@ class AFPRenderer(VerboseOutput):
only_depths: Optional[List[int]] = None,
only_frames: Optional[List[int]] = None,
movie_transform: Matrix = Matrix.identity(),
overridden_width: Optional[float] = None,
overridden_height: Optional[float] = None,
verbose: bool = False,
) -> Generator[Image.Image, None, None]:
# Given a path to a SWF root animation, attempt to render it to a list of frames.
@ -620,7 +650,7 @@ class AFPRenderer(VerboseOutput):
# This is the SWF we care about.
with self.debugging(verbose):
swf.color = background_color or swf.color
yield from self.__render(swf, only_depths, only_frames, movie_transform, background_image)
yield from self.__render(swf, only_depths, only_frames, movie_transform, background_image, overridden_width, overridden_height)
return
raise Exception(f'{path} not found in registered SWFs!')
@ -1642,6 +1672,8 @@ class AFPRenderer(VerboseOutput):
only_frames: Optional[List[int]],
movie_transform: Matrix,
background_image: Optional[List[Image.Image]],
overridden_width: Optional[float],
overridden_height: Optional[float],
) -> Generator[Image.Image, None, None]:
# First, let's attempt to resolve imports.
self.__registered_objects = self.__handle_imports(swf)
@ -1651,10 +1683,14 @@ class AFPRenderer(VerboseOutput):
frameno: int = 0
# Calculate actual size based on given movie transform.
resized_width, resized_height, _ = movie_transform.multiply_point(Point(swf.location.width, swf.location.height)).as_tuple()
actual_width = overridden_width or swf.location.width
actual_height = overridden_height or swf.location.height
resized_width, resized_height, _ = movie_transform.multiply_point(Point(actual_width, actual_height)).as_tuple()
# TODO: If the location top/left is nonzero, we need move the root transform
# so that the correct viewport is rendered.
if round(swf.location.top, 2) != 0.0 or round(swf.location.left, 2) != 0.0:
# TODO: If the location top/left is nonzero, we need move the root transform
# so that the correct viewport is rendered.
print("WARNING: Root clip requested to play not in top-left corner!")
# Create a root clip for the movie to play.
root_clip = PlacedClip(
@ -1674,6 +1710,10 @@ class AFPRenderer(VerboseOutput):
swf.labels,
),
)
root_clip._width = int(actual_width)
root_clip._height = int(actual_height)
last_width = actual_width
last_height = actual_height
self.__root = root_clip
# If we have a background image, add it to the root clip.
@ -1686,10 +1726,10 @@ class AFPRenderer(VerboseOutput):
imgwidth = background_image[0].width
imgheight = background_image[0].height
background_matrix = Matrix.affine(
a=swf.location.width / imgwidth,
a=actual_width / imgwidth,
b=0,
c=0,
d=swf.location.height / imgheight,
d=actual_height / imgheight,
tx=0,
ty=0,
)
@ -1772,6 +1812,12 @@ class AFPRenderer(VerboseOutput):
continue
if changed or last_rendered_frame is None:
if last_width != root_clip._width or last_height != root_clip._height:
last_width = root_clip._width
last_height = root_clip._height
if root_clip._width > actual_width or root_clip._height > actual_height:
print(f"WARNING: Root clip requested to resize to {last_width}x{last_height} which overflows root canvas!")
# Now, render out the placed objects.
color = swf.color or Color(0.0, 0.0, 0.0, 0.0)
curimage = Image.new("RGBA", (resized_width, resized_height), color=color.as_tuple())

View File

@ -567,6 +567,8 @@ def render_path(
background_loop_start: Optional[int] = None,
background_loop_end: Optional[int] = None,
background_loop_offset: Optional[int] = None,
override_width: Optional[int] = None,
override_height: Optional[int] = None,
force_width: Optional[int] = None,
force_height: Optional[int] = None,
force_aspect_ratio: Optional[str] = None,
@ -688,8 +690,16 @@ def render_path(
# Calculate the size of the animation so we can apply scaling options.
swf_location = renderer.compute_path_location(path)
requested_width = force_width if force_width is not None else swf_location.width
requested_height = force_height if force_height is not None else swf_location.height
if override_width is not None:
actual_width = float(override_width)
else:
actual_width = swf_location.width
if override_height is not None:
actual_height = float(override_height)
else:
actual_height = swf_location.height
requested_width = force_width if force_width is not None else actual_width
requested_height = force_height if force_height is not None else actual_height
# Allow overriding the aspect ratio.
if force_aspect_ratio:
@ -702,20 +712,20 @@ def render_path(
raise Exception("Ratio must only include positive numbers!")
actual_ratio = rx / ry
swf_ratio = swf_location.width / swf_location.height
swf_ratio = actual_width / actual_height
if abs(swf_ratio - actual_ratio) > 0.0001:
new_width = actual_ratio * swf_location.height
new_height = swf_location.width / actual_ratio
new_width = actual_ratio * actual_height
new_height = actual_width / actual_ratio
if new_width < swf_location.width and new_height < swf_location.height:
if new_width < actual_width and new_height < actual_height:
raise Exception("Impossible aspect ratio!")
if new_width > swf_location.width and new_height > swf_location.height:
if new_width > actual_width and new_height > actual_height:
raise Exception("Impossible aspect ratio!")
# We know that one is larger and one is smaller, pick the larger.
# This way we always stretch instead of shrinking.
if new_width > swf_location.width:
if new_width > actual_width:
requested_width = new_width
else:
requested_height = new_height
@ -726,10 +736,10 @@ def render_path(
# Calculate the overall view matrix based on the requested width/height.
transform = Matrix.affine(
a=requested_width / swf_location.width,
a=requested_width / actual_width,
b=0.0,
c=0.0,
d=requested_height / swf_location.height,
d=requested_height / actual_height,
tx=0.0,
ty=0.0,
)
@ -760,6 +770,8 @@ def render_path(
only_depths=requested_depths,
only_frames=requested_frames,
movie_transform=transform,
overridden_width=override_width,
overridden_height=override_height,
)
):
if show_progress:
@ -1147,6 +1159,24 @@ def main() -> int:
"the animation's original height it will be stretched vertically."
),
)
render_parser.add_argument(
"--override-width",
type=int,
default=None,
help=(
"Override the specified the width of the rendered animation to a specific pixel value, such as 640 or 800. Note that this performs "
"no scaling whatsoever. It simply overrides the animation's root canvas size."
),
)
render_parser.add_argument(
"--override-height",
type=int,
default=None,
help=(
"Override the specified the height of the rendered animation to a specific pixel value, such as 640 or 800. Note that this performs "
"no scaling whatsoever. It simply overrides the animation's root canvas size."
),
)
render_parser.add_argument(
"--force-aspect-ratio",
type=str,
@ -1247,6 +1277,8 @@ def main() -> int:
background_loop_start=args.background_loop_start,
background_loop_end=args.background_loop_end,
background_loop_offset=args.background_loop_offset,
override_width=args.override_width,
override_height=args.override_height,
force_width=args.force_width,
force_height=args.force_height,
force_aspect_ratio=args.force_aspect_ratio,