Please note that this will duplicate code found in 5.3.4, so this should go away when 5.3.4 comes out.
The zoom is unfiltered, so you can expect some aliasing, especially when zooming in. But filtering would kill performance, so unfiltered it is.
I'm now getting 50-70 fps on this, depending on what my computer is doing in the background.
I have included the ability to switch over to another image when the zoom is done, which may help as people won't be staring at the artifacts for very long.
I've attached a screenshot so people can see
The code is:
Code: Select all
init -100:
python:
import pygame
class _Zoom(renpy.display.core.Displayable):
"""
This displayable causes a zoom to take place, using image
scaling. The render of this displayable is always of the supplied
size. The child displayable is rendered, and a rectangle is
cropped out of it. This rectangle is interpolated between the
start and end rectangles. The rectangle is then scaled to the
supplied size. The zoom will take time seconds, after which it
will show the end rectangle, unless an after_child is
given.
The algorithm used for scaling does not perform any
interpolation or other smoothing.
"""
def __init__(self, size, start, end, time, child,
after_child=None, **properties):
"""
@param size: The size that the rectangle is scaled to, a
(width, height) tuple.
@param start: The start rectangle, an (xoffset, yoffset,
width, height) tuple.
@param end: The end rectangle, an (xoffset, yoffset,
width, height) tuple.
@param time: The amount of time it will take to
interpolate from the start to the end rectange.
@param child: The child displayable.
@param after_child: If present, a second child
widget. This displayable will be rendered after the zoom
completes. Use this to snap to a sharp displayable after
the zoom is done.
"""
super(_Zoom, self).__init__(**properties)
self.size = size
self.start = start
self.end = end
self.time = time
self.child = child
self.after_child = after_child
def predict(self, callback):
self.child.predict(callback)
if self.after_child:
self.after_child.predict(callback)
def render(self, width, height, st):
if self.time:
done = min(st / self.time, 1.0)
else:
done = 1.0
if self.after_child and done == 1.0:
return renpy.display.render.render(self.after_child, width, height, st)
rend = renpy.display.render.render(self.child, width, height, st)
surf = rend.pygame_surface()
rect = tuple([ (1.0 - done) * a + done * b for a, b in zip(self.start, self.end) ])
subsurf = surf.subsurface(rect)
scalesurf = pygame.transform.scale(subsurf, self.size)
renpy.display.render.mutated_surface(scalesurf)
rv = renpy.display.render.Render(self.size[0], self.size[1])
rv.blit(scalesurf, (0, 0))
rv.depends_on(rend)
if done < 1.0:
renpy.display.render.redraw(self, 0)
return rv
def event(self, ev, x, y):
return None
Zoom = renpy.curry(_Zoom)
label splashscreen:
scene bg whitehouse
"Before the zoom."
$ start_frames = config.frames
# This must be a scene or a hide/show pair. Just doing show bg
# whitehouse at ... would not work, at least in 5.3.3, due to some
# timebase issues.
scene bg whitehouse at Zoom((800, 600), (0, 0, 800, 600), (225, 150, 400, 300), 2.0)
$ renpy.pause(2.0)
$ fps = (config.frames - start_frames) / 2.0
"The zoom occured at %(fps).1f fps."
return

