Compare commits

...

6 Commits

Author SHA1 Message Date
Tom Rothamel 9b4821308d Change semantics of set_pan to be slightly more useful.
6.9.0e.
2009-03-09 23:12:04 -04:00
Tom Rothamel 663978906a Added renpy.partial. 2009-03-08 20:49:12 -04:00
Tom Rothamel 60f3308b8b Autobar now uses floating point numbers, making it smoother when the range is small.
Up the script version to 6.9.0.
2009-03-08 10:09:43 -04:00
Tom Rothamel a305d6e725 6.9.0d 2009-03-08 01:58:50 -05:00
Tom Rothamel 1feff344b8 New clipping code. 2009-03-07 21:23:01 -05:00
Tom Rothamel 4fa4ffae54 Releasing 6.9.0c. 2009-03-06 18:32:31 -05:00
13 changed files with 428 additions and 148 deletions
+9 -6
View File
@@ -117,16 +117,19 @@ Ren'Py now autosaves when entering the quit prompt, and will not actually
quit until this autosave is complete.
The default framerate limit (config.framerate) has been raised to
100fps. This make tearing less noticable than on a 60hz screen.
Added renpy.partial as a version of renpy.curry that requires two
function calls, rather than three.
Fixed a divide by zero error that occured when Alpha was used for 0
seconds.
The sound libraries have been upgraded:
* libvorbis is now at 1.2.0
* SDL_sound is now a development version, which includes the mpg123
decoder. According to the release announcment: ". Unlike mpglib, this
decoder seems to play mp3s that were recorded after 1999, too. :-)"
Libvorbis has been upgraded to version 1.2.0.
To reduce distribution size, OGG Speex support was dropped.
+1 -1
View File
@@ -22,7 +22,7 @@ label demo_multimedia:
# This plays a sound effect.
play sound "18005551212.ogg"
e "We can also play up to eight channels of sound effects on top of the music."
e "We can play two channels of sound effects on top of the music."
e "Voice support is included as part of Ren'Py, although we don't yet have a demonstration."
+1 -1
View File
@@ -1,3 +1,3 @@
init -999:
$ config.script_version = (6, 8, 0)
$ config.script_version = (6, 9, 0)
+1 -1
View File
@@ -27,7 +27,7 @@
# ***** ***** ***** ***** ***** ***** **** ***** ***** ***** *****
# Be sure to change script_version in launcher/script_version.rpy, too!
# Also check to see if we have to update renpy.py.
version = "Ren'Py 6.9.0b"
version = "Ren'Py 6.9.0e"
script_version = 5003000
savegame_suffix = "-LT1.save"
+2 -1
View File
@@ -80,7 +80,6 @@ if mix:
old_midi = mix.get_midi()
mixer_enabled = True
def disable_mixer():
global mixer_enabled
@@ -529,6 +528,8 @@ class Channel(object):
if pcm_ok:
self.pan_delay = delay
self.pan_time = _pan_time.get(self.number)
pss.set_pan(self.number, _pan.get(self.number, 0.0), self.pan_delay)
# The number of channels we support.
+2
View File
@@ -257,6 +257,8 @@ def bootstrap(renpy_base):
keep_running = False
except KeyboardInterrupt:
import traceback
traceback.print_exc()
break
except renpy.game.UtterRestartException:
+1 -1
View File
@@ -262,7 +262,7 @@ font_replacement_map = { }
with_callback = None
# The framerate limit, in frames per second.
framerate = 60
framerate = 100
# The number of frames that Ren'Py has shown.
frames = 0
+10
View File
@@ -59,3 +59,13 @@ def curry(fn):
"""
return Curry(Curry, fn)
def partial(function, *args, **kwargs):
"""
Stores the arguments and keyword arguments of function, and
returns something that, when called, calls the function with
a combination of the supplied arguments and the arguments of
the second call.
"""
return Curry(function, *args, **kwargs)
+6 -7
View File
@@ -818,7 +818,7 @@ class Display(object):
next_frame = self.next_frame
now = pygame.time.get_ticks()
frametime = 1000 / framerate
frametime = 1000.0 / framerate
# Handle timer rollover.
if next_frame > now + frametime:
@@ -829,10 +829,10 @@ class Display(object):
return False
# Otherwise, it is. Schedule the next frame.
if next_frame + frametime < now:
next_frame = now + frametime
else:
next_frame += frametime
# if next_frame + frametime < now:
next_frame = now + frametime
# else:
# next_frame += frametime
self.next_frame = next_frame
@@ -973,7 +973,7 @@ class Display(object):
updates.extend(self.draw_mouse(False))
damage = renpy.display.render.draw_screen(self.screen_xoffset, self.screen_yoffset)
damage = renpy.display.render.draw_screen(self.screen_xoffset, self.screen_yoffset, self.full_redraw)
if damage:
updates.extend(damage)
@@ -1105,7 +1105,6 @@ class Interface(object):
# Are we focused?
self.focused = True
# Properties for each layer.
self.layer_properties = { }
+15 -14
View File
@@ -188,7 +188,7 @@ class Cache(object):
surf = image.load()
has_alpha = surf.get_masks()[3]
surf = surf.convert_alpha(renpy.game.interface.display.window)
ce = CacheEntry(image, surf)
self.total_cache_size += ce.size
self.cache[image] = ce
@@ -202,26 +202,27 @@ class Cache(object):
# RLE detection. (ce.size is used to check that we're not
# 0 pixels big.)
if id(ce.surf) not in rle_cache and ce.size:
rle = image.rle
rle = not renpy.game.less_memory
# rle = image.rle
surf = ce.surf
# If we don't know if the image is RLE or not, guess.
# Only do so if the image has an alpha channel.
if rle is None and has_alpha and not renpy.game.less_memory:
sw, sh = surf.get_size()
for i in range(0, 10):
if surf.get_at((random.randint(0, sw-1),
random.randint(0, sh-1)))[3] == 0:
rle = True
break
# # If we don't know if the image is RLE or not, guess.
# # Only do so if the image has an alpha channel.
# if rle is None and has_alpha and not renpy.game.less_memory:
# sw, sh = surf.get_size()
# for i in range(0, 10):
# if surf.get_at((random.randint(0, sw-1),
# random.randint(0, sh-1)))[3] == 0:
# rle = True
# break
if rle:
# We must copy the surface, so we have a RLE-specific version.
rle_surf = ce.surf.convert_alpha(renpy.game.interface.display.window)
rle_surf.set_alpha(255, RLEACCEL)
rle_cache[id(ce.surf)] = rle_surf
renpy.display.render.mutated_surface(ce.surf)
if renpy.config.debug_image_cache:
+378 -114
View File
@@ -55,69 +55,6 @@ def free_memory():
render_cache.clear()
def render_screen(root, width, height):
"""
Renders `root` (a displayable) as the root of a screen with the given
`width` and `height`.
"""
global old_screen_render
global screen_render
global invalidated
old_screen_render = screen_render
rv = render(root, width, height, 0, 0)
screen_render = rv
invalidated = False
return rv
def draw_screen(xoffset, yoffset):
"""
Draws the render produced by render_screen to the screen.
"""
screen_render.is_opaque()
draw(pygame.display.get_surface(), screen_render, xoffset, yoffset, True)
return [ (xoffset, yoffset, screen_render.width, screen_render.height) ]
def kill_old_screen():
"""
Kills the old screen if it's different from the current screen.
"""
global old_screen_render
if old_screen_render is None or old_screen_render is screen_render:
return
old_screen_render.kill()
old_screen_render = None
def take_focuses(focuses):
"""
Adds a list of rectangular focus regions to the focuses list.
"""
screen_render.take_focuses(IDENTITY, 0, 0, focuses)
def focus_at_point(x, y):
"""
Returns a focus object corresponding to the uppermost displayable
at point, or None if nothing focusable is at point.
"""
cf = screen_render.focus_at_point(x, y)
if cf is None:
return None
else:
d, arg = cf
return renpy.display.focus.Focus(d, arg, None, None, None, None)
def render(d, width, height, st, at):
"""
Causes the displayable `d` to be rendered in an area of size
@@ -244,17 +181,6 @@ def redraw(d, when):
redraw_queue.append((when + renpy.game.interface.frame_time, d))
# TODO: The master function that redraws the screen.
def mutated_surface(surf):
"""
Called to indicate that the given surface has changed. (Does nothing
right now.)
"""
# Doesn't do anything anymore, but keep it in case we need it again,
# and for compatibility with the public api.
class Matrix2D(object):
"""
This represents a 2d matrix that can be used to transform
@@ -278,14 +204,206 @@ class Matrix2D(object):
other.ydx * self.xdy + other.ydy * self.ydy)
IDENTITY = Matrix2D(1, 0, 0, 1)
def draw(dest, what, xo, yo, screen):
class Clipper(object):
"""
This is used to calculate the clipping rectangle and update rectangles
used for a particular draw of the screen.
"""
def __init__(self):
# Lists of (x0, y0, x1, y1, clip, surface, transform) tuples,
# representing how a displayable is drawn to the screen.
self.blits = [ ]
self.old_blits = [ ]
# Sets of (x0, y0, x1, y1) tuples, representing areas that
# aren't part of any displayable.
self.forced = set()
self.old_forced = set()
# The set of surfaces that have been mutated recently.
self.mutated = set()
def compute(self, full_redraw):
"""
This returns a clipping rectangle, and a list of update rectangles
that cover the changes between the old and new frames.
"""
# First, get things out of the fields, and update them. This
# allows us to just return without having to do any cleanup
# code.
bl0 = self.old_blits
bl1 = self.blits
old_forced = self.old_forced
forced = self.forced
mutated = self.mutated
self.old_blits = bl1
self.blits = [ ]
self.old_forced = forced
self.forced = set()
self.mutated = set()
sw = renpy.config.screen_width
sh = renpy.config.screen_height
sa = sw * sh
# A tuple representing the size of the fullscreen.
fullscreen = (0, 0, sw, sh)
# Check to see if a full redraw has been forced, and return
# early.
if full_redraw:
return fullscreen, [ fullscreen ]
# Quick checks to see if a dissolve is happening, or something like
# that.
changes = forced | old_forced
if fullscreen in changes:
return fullscreen, [ fullscreen ]
# Compute the differences between the two sets, and add those
# to changes.
i0 = 0
i1 = 0
bl1set = set(bl1)
while True:
if i0 >= len(bl0) or i1 >= len(bl1):
break
b0 = bl0[i0]
b1 = bl1[i1]
if b0 == b1:
if id(b0[5]) in mutated:
changes.add(b0[:5])
i0 += 1
i1 += 1
elif b0 not in bl1set:
changes.add(b0[:5])
i0 += 1
else:
changes.add(b1[:5])
i1 += 1
changes.update(i[:5] for i in bl0[i0:])
changes.update(i[:5] for i in bl1[i1:])
# No changes? Quit.
if not changes:
return None, [ ]
# Compute the sizes of the updated rectangles.
sized = [ ]
for x0, y0, x1, y1, (sx0, sy0, sx1, sy1) in changes:
if x0 < sx0:
x0 = sx0
if y0 < sy0:
y0 = sy0
if x1 > sx1:
x1 = sx1
if y1 > sy1:
y1 = sy1
w = x1 - x0
h = y1 - y0
area = w * h
if area >= sa:
return fullscreen, [ fullscreen ]
sized.append((area, x0, y0, x1, y1))
sized.sort()
# The list of non-contiguous updates.
noncont = [ ]
# The total area of noncont.
nca = 0
# Pick the largest area, merge with all overlapping smaller areas, repeat
# until no merge possible.
while sized:
area, x0, y0, x1, y1 = sized.pop()
merged = False
if nca + area >= sa:
return (0, 0, sw, sh), [ (0, 0, sw, sh) ]
i = 0
while i < len(sized):
iarea, ix0, iy0, ix1, iy1 = sized[i]
if (x0 <= ix0 <= x1 or x0 <= ix1 <= x1) and \
(y0 <= iy0 <= y1 or y0 <= iy1 <= y1):
merged = True
x0 = min(x0, ix0)
x1 = max(x1, ix1)
y0 = min(y0, iy0)
y1 = max(y1, iy1)
area = (x1 - x0) * (y1 - y0)
sized.pop(i)
else:
i += 1
if merged:
sized.append((area, x0, y0, x1, y1))
else:
noncont.append((x0, y0, x1, y1))
nca += area
x0, y0, x1, y1 = noncont.pop()
x0 = int(x0)
y0 = int(y0)
x1 = int(math.ceil(x1))
y1 = int(math.ceil(y1))
# A list of (x, y, w, h) tuples for each update.
updates = [ (x0, y0, x1 - x0, y1 - y0) ]
for ix0, iy0, ix1, iy1 in noncont:
ix0 = int(ix0)
iy0 = int(iy0)
ix1 = int(math.ceil(ix1))
iy1 = int(math.ceil(iy1))
x0 = min(x0, ix0)
y0 = min(y0, iy0)
x1 = max(x1, ix1)
y1 = max(y1, iy1)
updates.append((ix0, iy0, ix1 - ix0, iy1 - iy0))
return (x0, y0, x1 - x0, y1 - y0), updates
clippers = [ Clipper() ]
def draw(dest, clip, what, xo, yo, screen):
"""
This is the simple draw routine, which only works when alpha is 1.0
and the matrices are None. If those aren't the case, draw_complex
is used instead.
`dest` - The destination surface.
`dest` - Either a destination surface, or a clipper.
`clip` - If None, we should draw. Otherwise we should clip, and this is
the rectangle to clip to.
`what` - The Render or Surface we're drawing to.
`xo` - The X offset.
`yo` - The Y offset.
@@ -299,13 +417,21 @@ def draw(dest, what, xo, yo, screen):
if screen:
what = renpy.display.im.rle_cache.get(id(what), what)
blit_lock.acquire()
dest.blit(what, (xo, yo))
blit_lock.release()
if clip:
w, h = what.get_size()
dest.blits.append((xo, yo, xo + w, yo + h, clip, what, None))
else:
blit_lock.acquire()
dest.blit(what, (xo, yo))
blit_lock.release()
# Subpixel blit.
else:
renpy.display.module.subpixel(what, dest, xo, yo)
if clip:
w, h = what.get_size()
dest.blits.append((xo, yo, xo + w, yo + h, clip, what, None))
else:
renpy.display.module.subpixel(what, dest, xo, yo)
return
@@ -315,7 +441,12 @@ def draw(dest, what, xo, yo, screen):
xo = int(xo)
yo = int(yo)
dw, dh = dest.get_size()
if clip:
dx0, dy0, dx1, dy1 = clip
dw = dx1 - dx0
dh = dy1 - dy0
else:
dw, dh = dest.get_size()
if xo >= 0:
newx = 0
@@ -344,44 +475,71 @@ def draw(dest, what, xo, yo, screen):
if subw <= 0 or subh <= 0:
return
newdest = dest.subsurface((subx, suby, subw, subh))
what.draw_func(newdest, newx, newy)
if clip:
dest.forced.add((subx, suby, subx + subw, suby + subh, clip))
else:
newdest = dest.subsurface((subx, suby, subw, subh))
what.draw_func(newdest, newx, newy)
return
# Deal with clipping, if necessary.
if what.clipping:
width = what.width
height = what.height
if xo < 0:
width = width + xo
xo = 0
if yo < 0:
height = height + yo
yo = 0
if clip:
cx0, cy0, cx1, cy1 = clip
dw, dh = dest.get_size()
width = min(dw - xo, width)
height = min(dh - yo, height)
cx0 = max(cx0, xo)
cy0 = max(cy0, yo)
cx1 = min(cx1, xo + what.width)
cy1 = min(cy1, yo + what.height)
if cx0 > cx1 or cy0 > cy1:
return
dest = dest.subsurface((xo, yo, width, height))
xo = 0
yo = 0
clip = (cx0, cy0, cx1, cy1)
else:
# After this code, x and y are the coordinates of the subsurface
# relative to the destination. xo and yo are the offset of the
# upper-left corner relative to the subsurface.
if xo >= 0:
x = xo
xo = 0
else:
x = 0
# xo = xo
if yo >= 0:
y = yo
yo = 0
else:
y = 0
# yo = yo
dw, dh = dest.get_size()
width = min(dw - x, what.width + xo)
height = min(dh - y, what.height + yo)
if width < 0 or height < 0:
return
dest = dest.subsurface((x, y, width, height))
# Deal with alpha and transforms by passing them off to draw_transformed.
if what.alpha != 1 or what.forward:
for child, cxo, cyo, focus, main in what.visible_children:
draw_transformed(dest, child, xo + cxo, yo + cyo,
draw_transformed(dest, clip, child, xo + cxo, yo + cyo,
what.alpha, what.forward, what.reverse)
return
for child, cxo, cyo, focus, main in what.visible_children:
draw(dest, child, xo + cxo, yo + cyo, screen)
draw(dest, clip, child, xo + cxo, yo + cyo, screen)
def draw_transformed(dest, what, xo, yo, alpha, forward, reverse):
def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
if not isinstance(what, Render):
@@ -391,7 +549,12 @@ def draw_transformed(dest, what, xo, yo, alpha, forward, reverse):
# Figure out where the other corner of the transformed surface
# is on the screen.
sw, sh = what.get_size()
dw, dh = dest.get_size()
if clip:
dx0, dy0, dx1, dy1 = clip
dw = dx1 - dx0
dh = dy1 - dy0
else:
dw, dh = dest.get_size()
x0, y0 = 0, 0
x1, y1 = reverse.transform(sw, 0)
@@ -414,15 +577,25 @@ def draw_transformed(dest, what, xo, yo, alpha, forward, reverse):
maxy = dh
cx, cy = forward.transform(minx - xo, miny - yo)
dest = dest.subsurface((minx, miny, maxx - minx, maxy - miny))
renpy.display.module.alpha_transform(
what, dest,
cx, cy,
forward.xdx, forward.ydx,
forward.xdy, forward.ydy,
alpha)
if clip:
dest.blits.append(
(minx, miny, maxx, maxy, clip, what,
(cx, cy,
forward.xdx, forward.ydx,
forward.xdy, forward.ydy,
alpha)))
else:
dest = dest.subsurface((minx, miny, maxx - minx, maxy - miny))
renpy.display.module.alpha_transform(
what, dest,
cx, cy,
forward.xdx, forward.ydx,
forward.xdy, forward.ydy,
alpha)
return
@@ -443,7 +616,98 @@ def draw_transformed(dest, what, xo, yo, alpha, forward, reverse):
child_forward = forward
child_reverse = reverse
draw_transformed(dest, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
def render_screen(root, width, height):
"""
Renders `root` (a displayable) as the root of a screen with the given
`width` and `height`.
"""
global old_screen_render
global screen_render
global invalidated
old_screen_render = screen_render
rv = render(root, width, height, 0, 0)
screen_render = rv
invalidated = False
return rv
def draw_screen(xoffset, yoffset, full_redraw):
"""
Draws the render produced by render_screen to the screen.
"""
screen_render.is_opaque()
clip = (xoffset, yoffset, xoffset + screen_render.width, yoffset + screen_render.height)
clipper = clippers[0]
draw(clipper, clip, screen_render, xoffset, yoffset, True)
cliprect, updates = clipper.compute(full_redraw)
# print "CR", cliprect
# print "UD", updates
if cliprect is None:
return [ ]
x, y, w, h = cliprect
dest = pygame.display.get_surface().subsurface(cliprect)
draw(dest, None, screen_render, -x, -y, True)
return updates
def kill_old_screen():
"""
Kills the old screen if it's different from the current screen.
"""
global old_screen_render
if old_screen_render is None or old_screen_render is screen_render:
return
old_screen_render.kill()
old_screen_render = None
def take_focuses(focuses):
"""
Adds a list of rectangular focus regions to the focuses list.
"""
screen_render.take_focuses(IDENTITY, 0, 0, focuses)
def focus_at_point(x, y):
"""
Returns a focus object corresponding to the uppermost displayable
at point, or None if nothing focusable is at point.
"""
cf = screen_render.focus_at_point(x, y)
if cf is None:
return None
else:
d, arg = cf
return renpy.display.focus.Focus(d, arg, None, None, None, None)
def mutated_surface(surf):
"""
Called to indicate that the given surface has changed.
"""
for i in clippers:
i.mutated.add(id(surf))
class Render(object):
@@ -622,7 +886,7 @@ class Render(object):
self.is_opaque()
rv = pygame.Surface((self.width, self.height), 0, sample)
draw(rv, self, 0, 0, False)
draw(rv, None, self, 0, 0, False)
# Stash and return the surface.
if alpha:
+1 -1
View File
@@ -33,7 +33,7 @@ from renpy.display.font import register_sfont, register_mudgefont, register_bmfo
from renpy.display.behavior import Keymap
from renpy.display.minigame import Minigame
from renpy.curry import curry
from renpy.curry import curry, partial
from renpy.audio.sound import play
from renpy.display.video import movie_start_fullscreen, movie_start_displayable, movie_stop
from renpy.loadsave import load, save, list_saved_games, can_load, rename_save, unlink_save, scan_saved_game
+1 -1
View File
@@ -384,7 +384,7 @@ def _autobar_interpolate(range, start, end, time, st, at, **properties):
t = st / time
redraw = 0
value = type(start)(start + t * (end - start))
value = start + t * (end - start)
return renpy.display.behavior.Bar(range, value, None, None, **properties), redraw
autobar_interpolate = renpy.curry.curry(_autobar_interpolate)