Implement ability to override the canvas size, to render animations that overflow the canvas.
This commit is contained in:
parent
9dd3772380
commit
d39a660eab
bemani
@ -191,6 +191,10 @@ class PlacedClip(PlacedObject):
|
|||||||
self.requested_frame: Optional[int] = None
|
self.requested_frame: Optional[int] = None
|
||||||
self.visible_frame: int = -1
|
self.visible_frame: int = -1
|
||||||
|
|
||||||
|
# Root clip resizing, which we don't really support.
|
||||||
|
self.__width = 0
|
||||||
|
self.__height = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self) -> RegisteredClip:
|
def source(self) -> RegisteredClip:
|
||||||
return self.__source
|
return self.__source
|
||||||
@ -299,6 +303,30 @@ class PlacedClip(PlacedObject):
|
|||||||
def _visible(self, val: Any) -> None:
|
def _visible(self, val: Any) -> None:
|
||||||
self.visible = val != 0
|
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):
|
class PlacedImage(PlacedObject):
|
||||||
# An image that occupies its parent clip at some depth. Placed by an AP2PlaceObjectTag
|
# 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_depths: Optional[List[int]] = None,
|
||||||
only_frames: Optional[List[int]] = None,
|
only_frames: Optional[List[int]] = None,
|
||||||
movie_transform: Matrix = Matrix.identity(),
|
movie_transform: Matrix = Matrix.identity(),
|
||||||
|
overridden_width: Optional[float] = None,
|
||||||
|
overridden_height: Optional[float] = None,
|
||||||
verbose: bool = False,
|
verbose: bool = False,
|
||||||
) -> Generator[Image.Image, None, None]:
|
) -> Generator[Image.Image, None, None]:
|
||||||
# Given a path to a SWF root animation, attempt to render it to a list of frames.
|
# 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.
|
# This is the SWF we care about.
|
||||||
with self.debugging(verbose):
|
with self.debugging(verbose):
|
||||||
swf.color = background_color or swf.color
|
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
|
return
|
||||||
|
|
||||||
raise Exception(f'{path} not found in registered SWFs!')
|
raise Exception(f'{path} not found in registered SWFs!')
|
||||||
@ -1642,6 +1672,8 @@ class AFPRenderer(VerboseOutput):
|
|||||||
only_frames: Optional[List[int]],
|
only_frames: Optional[List[int]],
|
||||||
movie_transform: Matrix,
|
movie_transform: Matrix,
|
||||||
background_image: Optional[List[Image.Image]],
|
background_image: Optional[List[Image.Image]],
|
||||||
|
overridden_width: Optional[float],
|
||||||
|
overridden_height: Optional[float],
|
||||||
) -> Generator[Image.Image, None, None]:
|
) -> Generator[Image.Image, None, None]:
|
||||||
# First, let's attempt to resolve imports.
|
# First, let's attempt to resolve imports.
|
||||||
self.__registered_objects = self.__handle_imports(swf)
|
self.__registered_objects = self.__handle_imports(swf)
|
||||||
@ -1651,10 +1683,14 @@ class AFPRenderer(VerboseOutput):
|
|||||||
frameno: int = 0
|
frameno: int = 0
|
||||||
|
|
||||||
# Calculate actual size based on given movie transform.
|
# 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
|
if round(swf.location.top, 2) != 0.0 or round(swf.location.left, 2) != 0.0:
|
||||||
# so that the correct viewport is rendered.
|
# 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.
|
# Create a root clip for the movie to play.
|
||||||
root_clip = PlacedClip(
|
root_clip = PlacedClip(
|
||||||
@ -1674,6 +1710,10 @@ class AFPRenderer(VerboseOutput):
|
|||||||
swf.labels,
|
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
|
self.__root = root_clip
|
||||||
|
|
||||||
# If we have a background image, add it to the 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
|
imgwidth = background_image[0].width
|
||||||
imgheight = background_image[0].height
|
imgheight = background_image[0].height
|
||||||
background_matrix = Matrix.affine(
|
background_matrix = Matrix.affine(
|
||||||
a=swf.location.width / imgwidth,
|
a=actual_width / imgwidth,
|
||||||
b=0,
|
b=0,
|
||||||
c=0,
|
c=0,
|
||||||
d=swf.location.height / imgheight,
|
d=actual_height / imgheight,
|
||||||
tx=0,
|
tx=0,
|
||||||
ty=0,
|
ty=0,
|
||||||
)
|
)
|
||||||
@ -1772,6 +1812,12 @@ class AFPRenderer(VerboseOutput):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if changed or last_rendered_frame is None:
|
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.
|
# Now, render out the placed objects.
|
||||||
color = swf.color or Color(0.0, 0.0, 0.0, 0.0)
|
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())
|
curimage = Image.new("RGBA", (resized_width, resized_height), color=color.as_tuple())
|
||||||
|
@ -567,6 +567,8 @@ def render_path(
|
|||||||
background_loop_start: Optional[int] = None,
|
background_loop_start: Optional[int] = None,
|
||||||
background_loop_end: Optional[int] = None,
|
background_loop_end: Optional[int] = None,
|
||||||
background_loop_offset: 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_width: Optional[int] = None,
|
||||||
force_height: Optional[int] = None,
|
force_height: Optional[int] = None,
|
||||||
force_aspect_ratio: Optional[str] = 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.
|
# Calculate the size of the animation so we can apply scaling options.
|
||||||
swf_location = renderer.compute_path_location(path)
|
swf_location = renderer.compute_path_location(path)
|
||||||
requested_width = force_width if force_width is not None else swf_location.width
|
if override_width is not None:
|
||||||
requested_height = force_height if force_height is not None else swf_location.height
|
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.
|
# Allow overriding the aspect ratio.
|
||||||
if force_aspect_ratio:
|
if force_aspect_ratio:
|
||||||
@ -702,20 +712,20 @@ def render_path(
|
|||||||
raise Exception("Ratio must only include positive numbers!")
|
raise Exception("Ratio must only include positive numbers!")
|
||||||
|
|
||||||
actual_ratio = rx / ry
|
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:
|
if abs(swf_ratio - actual_ratio) > 0.0001:
|
||||||
new_width = actual_ratio * swf_location.height
|
new_width = actual_ratio * actual_height
|
||||||
new_height = swf_location.width / actual_ratio
|
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!")
|
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!")
|
raise Exception("Impossible aspect ratio!")
|
||||||
|
|
||||||
# We know that one is larger and one is smaller, pick the larger.
|
# We know that one is larger and one is smaller, pick the larger.
|
||||||
# This way we always stretch instead of shrinking.
|
# This way we always stretch instead of shrinking.
|
||||||
if new_width > swf_location.width:
|
if new_width > actual_width:
|
||||||
requested_width = new_width
|
requested_width = new_width
|
||||||
else:
|
else:
|
||||||
requested_height = new_height
|
requested_height = new_height
|
||||||
@ -726,10 +736,10 @@ def render_path(
|
|||||||
|
|
||||||
# Calculate the overall view matrix based on the requested width/height.
|
# Calculate the overall view matrix based on the requested width/height.
|
||||||
transform = Matrix.affine(
|
transform = Matrix.affine(
|
||||||
a=requested_width / swf_location.width,
|
a=requested_width / actual_width,
|
||||||
b=0.0,
|
b=0.0,
|
||||||
c=0.0,
|
c=0.0,
|
||||||
d=requested_height / swf_location.height,
|
d=requested_height / actual_height,
|
||||||
tx=0.0,
|
tx=0.0,
|
||||||
ty=0.0,
|
ty=0.0,
|
||||||
)
|
)
|
||||||
@ -760,6 +770,8 @@ def render_path(
|
|||||||
only_depths=requested_depths,
|
only_depths=requested_depths,
|
||||||
only_frames=requested_frames,
|
only_frames=requested_frames,
|
||||||
movie_transform=transform,
|
movie_transform=transform,
|
||||||
|
overridden_width=override_width,
|
||||||
|
overridden_height=override_height,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
if show_progress:
|
if show_progress:
|
||||||
@ -1147,6 +1159,24 @@ def main() -> int:
|
|||||||
"the animation's original height it will be stretched vertically."
|
"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(
|
render_parser.add_argument(
|
||||||
"--force-aspect-ratio",
|
"--force-aspect-ratio",
|
||||||
type=str,
|
type=str,
|
||||||
@ -1247,6 +1277,8 @@ def main() -> int:
|
|||||||
background_loop_start=args.background_loop_start,
|
background_loop_start=args.background_loop_start,
|
||||||
background_loop_end=args.background_loop_end,
|
background_loop_end=args.background_loop_end,
|
||||||
background_loop_offset=args.background_loop_offset,
|
background_loop_offset=args.background_loop_offset,
|
||||||
|
override_width=args.override_width,
|
||||||
|
override_height=args.override_height,
|
||||||
force_width=args.force_width,
|
force_width=args.force_width,
|
||||||
force_height=args.force_height,
|
force_height=args.force_height,
|
||||||
force_aspect_ratio=args.force_aspect_ratio,
|
force_aspect_ratio=args.force_aspect_ratio,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user