summaryrefslogtreecommitdiff
path: root/unrpyc/renpy/display/swdraw.py
diff options
context:
space:
mode:
Diffstat (limited to 'unrpyc/renpy/display/swdraw.py')
-rw-r--r--unrpyc/renpy/display/swdraw.py1102
1 files changed, 1102 insertions, 0 deletions
diff --git a/unrpyc/renpy/display/swdraw.py b/unrpyc/renpy/display/swdraw.py
new file mode 100644
index 0000000..d3ecb51
--- /dev/null
+++ b/unrpyc/renpy/display/swdraw.py
@@ -0,0 +1,1102 @@
+# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import renpy.display
+import pygame
+import math
+import weakref
+import time
+import os
+
+from renpy.display.render import blit_lock, IDENTITY, BLIT, DISSOLVE, IMAGEDISSOLVE, PIXELLATE
+
+# A map from cached surface to rle version of cached surface.
+rle_cache = weakref.WeakKeyDictionary()
+
+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:
+
+ # Round up by a pixel, to prevent visual artifacts when scaled down.
+ x1 += 1
+ y1 += 1
+
+ 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
+
+ if w <= 0 or h <= 0:
+ continue
+
+ 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
+
+ if not noncont:
+ return None, [ ]
+
+ 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 surface(w, h, alpha):
+ """
+ Creates a surface that shares a pixel format with the screen. The created
+ surface will
+ """
+
+ if alpha:
+ rv = pygame.Surface((w + 4, h + 4), pygame.SRCALPHA)
+ else:
+ rv = pygame.Surface((w + 4, h + 4), 0)
+
+ return rv.subsurface((2, 2, w, h))
+
+def copy_surface(surf):
+ w, h = surf.get_size()
+ rv = surface(w, h, True)
+
+ renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
+ return rv
+
+def draw_special(what, dest, x, y):
+ """
+ This handles the special drawing operations, such as dissolve and
+ image dissolve. `x` and `y` are the offsets of the thing to be drawn
+ relative to the destination rectangle, and are always negative.
+ """
+
+ dw, dh = dest.get_size()
+
+ w = min(dw, what.width + x)
+ h = min(dh, what.height + y)
+
+ if w <= 0 or h <= 0:
+ return
+
+ if what.operation == DISSOLVE:
+
+ bottom = what.children[0][0].render_to_texture(True)
+ top = what.children[1][0].render_to_texture(True)
+
+ if what.operation_alpha:
+ target = surface(w, h, True)
+ else:
+ target = dest.subsurface((0, 0, w, h))
+
+ renpy.display.module.blend(
+ bottom.subsurface((-x, -y, w, h)),
+ top.subsurface((-x, -y, w, h)),
+ target,
+ int(what.operation_complete * 255))
+
+ if what.operation_alpha:
+ dest.blit(target, (0, 0))
+
+ elif what.operation == IMAGEDISSOLVE:
+
+ image = what.children[0][0].render_to_texture(True)
+ bottom = what.children[1][0].render_to_texture(True)
+ top = what.children[2][0].render_to_texture(True)
+
+ if what.operation_alpha:
+ target = surface(w, h, True)
+ else:
+ target = dest.subsurface((0, 0, w, h))
+
+ ramplen = what.operation_parameter
+
+ ramp = "\x00" * 256
+
+ for i in range(0, ramplen):
+ ramp += chr(255 * i / ramplen)
+
+ ramp += "\xff" * 256
+
+ step = int( what.operation_complete * (256 + ramplen) )
+ ramp = ramp[step:step+256]
+
+ renpy.display.module.imageblend(
+ bottom.subsurface((-x, -y, w, h)),
+ top.subsurface((-x, -y, w, h)),
+ target,
+ image.subsurface((-x, -y, w, h)),
+ ramp)
+
+ if what.operation_alpha:
+ dest.blit(target, (0, 0))
+
+ elif what.operation == PIXELLATE:
+
+ surf = what.children[0][0].render_to_texture(False)
+
+ px = what.operation_parameter
+
+ renpy.display.module.pixellate(
+ surf.subsurface((-x, -y, w, h)),
+ dest.subsurface((0, 0, w, h)),
+ px, px, px, px)
+
+ else:
+ raise Exception("Unknown operation: %d" % what.operation)
+
+
+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` - 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.
+ `screen` - True if this is a blit to the screen, False otherwise.
+ """
+
+ if not isinstance(what, renpy.display.render.Render):
+
+ # Pixel-Aligned blit.
+ if isinstance(xo, int) and isinstance(yo, int):
+ if screen:
+ what = rle_cache.get(what, what)
+
+ if clip:
+ w, h = what.get_size()
+ dest.blits.append((xo, yo, xo + w, yo + h, clip, what, None))
+ else:
+ try:
+ blit_lock.acquire()
+ dest.blit(what, (xo, yo))
+ finally:
+ blit_lock.release()
+
+ # Subpixel blit.
+ else:
+ 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
+
+ # Deal with draw functions.
+ if what.operation != BLIT:
+
+ xo = int(xo)
+ yo = int(yo)
+
+ if clip:
+ dx0, dy0, dx1, dy1 = clip
+ dw = dx1 - dx0
+ dh = dy1 - dy0
+ else:
+ dw, dh = dest.get_size()
+
+ if xo >= 0:
+ newx = 0
+ subx = xo
+ else:
+ newx = xo
+ subx = 0
+
+ if yo >= 0:
+ newy = 0
+ suby = yo
+ else:
+ newy = yo
+ suby = 0
+
+ if subx >= dw or suby >= dh:
+ return
+
+ # newx and newy are the offset of this render relative to the
+ # subsurface. They can only be negative or 0, as otherwise we
+ # would make a smaller subsurface.
+
+ subw = min(dw - subx, what.width + newx)
+ subh = min(dh - suby, what.height + newy)
+
+ if subw <= 0 or subh <= 0:
+ return
+
+ 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)
+ draw_special(what, newdest, newx, newy)
+
+
+ return
+
+ # Deal with clipping, if necessary.
+ if what.clipping:
+
+ if clip:
+ cx0, cy0, cx1, cy1 = clip
+
+ 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
+
+ clip = (cx0, cy0, cx1, cy1)
+
+ dest.forced.add(clip + (clip,))
+ return
+
+ 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 is not None and what.forward is not IDENTITY):
+ for child, cxo, cyo, _focus, _main in what.visible_children:
+ 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, clip, child, xo + cxo, yo + cyo, screen)
+
+def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
+
+ # If our alpha has hit 0, don't do anything.
+ if alpha <= 0.003: # (1 / 256)
+ return
+
+ if forward is None:
+ forward = IDENTITY
+ reverse = IDENTITY
+
+ if not isinstance(what, renpy.display.render.Render):
+
+ # Figure out where the other corner of the transformed surface
+ # is on the screen.
+ sw, sh = what.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, 0.0
+ x1, y1 = reverse.transform(sw, 0.0)
+ x2, y2 = reverse.transform(sw, sh)
+ x3, y3 = reverse.transform(0.0, sh)
+
+ minx = math.floor(min(x0, x1, x2, x3) + xo)
+ maxx = math.ceil(max(x0, x1, x2, x3) + xo)
+ miny = math.floor(min(y0, y1, y2, y3) + yo)
+ maxy = math.ceil(max(y0, y1, y2, y3) + yo)
+
+ if minx < 0:
+ minx = 0
+ if miny < 0:
+ miny = 0
+
+ if maxx > dw:
+ maxx = dw
+ if maxy > dh:
+ maxy = dh
+
+ if minx > dw or miny > dh or maxx < 0 or maxy < 0:
+ return
+
+ cx, cy = forward.transform(minx - xo, miny - yo)
+
+ if clip:
+
+ dest.blits.append(
+ (minx, miny, maxx + dx0, maxy + dy0, 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.transform(
+ what, dest,
+ cx, cy,
+ forward.xdx, forward.ydx,
+ forward.xdy, forward.ydy,
+ alpha, True)
+
+ return
+
+ if what.clipping:
+
+ if reverse.xdy or reverse.ydx:
+ draw_transformed(dest, clip, what.pygame_surface(True), xo, yo, alpha, forward, reverse)
+ return
+
+ width = what.width * reverse.xdx
+ height = what.height * reverse.ydy
+
+ if clip:
+ cx0, cy0, cx1, cy1 = clip
+
+ cx0 = max(cx0, xo)
+ cy0 = max(cy0, yo)
+ cx1 = min(cx1, xo + width)
+ cy1 = min(cy1, yo + height)
+
+ if cx0 > cx1 or cy0 > cy1:
+ return
+
+ clip = (cx0, cy0, cx1, cy1)
+
+ dest.forced.add(clip + (clip,))
+ return
+
+ 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, width + xo)
+ height = min(dh - y, height + yo)
+
+ if width < 0 or height < 0:
+ return
+
+ dest = dest.subsurface((x, y, width, height))
+
+ if what.draw_func or what.operation != BLIT:
+ child = what.pygame_surface(True)
+ draw_transformed(dest, clip, child, xo, yo, alpha, forward, reverse)
+ return
+
+ for child, cxo, cyo, _focus, _main in what.visible_children:
+
+ cxo, cyo = reverse.transform(cxo, cyo)
+
+ if what.forward:
+ child_forward = forward * what.forward
+ child_reverse = what.reverse * reverse
+ else:
+ child_forward = forward
+ child_reverse = reverse
+
+ draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
+
+
+
+def do_draw_screen(screen_render, full_redraw, swdraw):
+ """
+ Draws the render produced by render_screen to the screen.
+ """
+
+ yoffset = xoffset = 0
+
+ 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)
+
+ if cliprect is None:
+ return [ ]
+
+ x, y, _w, _h = cliprect
+
+ dest = swdraw.window.subsurface(cliprect)
+ draw(dest, None, screen_render, -x, -y, True)
+
+ return updates
+
+
+class SWDraw(object):
+ """
+ This uses the software renderer to draw to the screen.
+ """
+
+ def __init__(self):
+ self.display_info = None
+
+ self.reset()
+
+ def reset(self):
+
+ # Should we draw the screen?
+ self.suppressed_blit = False
+
+ # The earliest time at which the next frame can be redrawn.
+ self.next_frame = 0
+
+ # Mouse re-drawing.
+ self.mouse_location = None
+ self.mouse_backing = None
+ self.mouse_backing_pos = None
+ self.mouse_info = None
+
+
+ # Is the mouse currently visible?
+ self.mouse_old_visible = None
+
+ # This is used to cache the surface->texture operation.
+ self.texture_cache = weakref.WeakKeyDictionary()
+
+ # This is used to display video to the screen.
+ self.fullscreen_surface = None
+
+ # Info.
+ self.info = { "renderer" : "sw", "resizable" : False }
+
+ pygame.display.init()
+ renpy.display.interface.post_init()
+
+ if self.display_info is None:
+ self.display_info = pygame.display.Info()
+
+ # The scale factor we use for this display.
+ self.scale_factor = 1.0
+
+ # Should we scale fast, or scale good-looking?
+ self.scale_fast = "RENPY_SCALE_FAST" in os.environ
+
+ # The screen returned to us from pygame.
+ self.screen = None
+
+ # The window that we render into, if not the screen. This has a
+ # 1px border around it iff we're scaling.
+ self.window = None
+
+ def set_mode(self, virtual_size, physical_size, fullscreen):
+
+ # Reset before resize.
+ renpy.display.interface.kill_textures_and_surfaces()
+ self.reset()
+
+ width, height = virtual_size
+
+ # Set up scaling, if necessary.
+ screen_width = self.display_info.current_w
+ screen_height = self.display_info.current_h
+
+ if not fullscreen:
+ screen_height -= 102
+ screen_width -= 102
+
+ scale_factor = min(1.0 * screen_width / width, 1.0 * screen_height / height, 1.0)
+ if "RENPY_SCALE_FACTOR" in os.environ:
+ scale_factor = float(os.environ["RENPY_SCALE_FACTOR"])
+ self.scale_factor = scale_factor
+
+ # Figure out the fullscreen info.
+ if fullscreen:
+ fsflag = pygame.FULLSCREEN
+ else:
+ fsflag = 0
+
+ # If a window exists of the right size and flags, use it. Otherwise,
+ # make our own window.
+ old_screen = pygame.display.get_surface()
+
+ scaled_width = int(width * scale_factor)
+ scaled_height = int(height * scale_factor)
+
+ if ((old_screen is not None) and
+ (old_screen.get_size() == (scaled_width, scaled_height)) and
+ (old_screen.get_flags() & pygame.FULLSCREEN == fsflag)):
+
+ self.screen = old_screen
+
+ else:
+ self.screen = pygame.display.set_mode((scaled_width, scaled_height), fsflag, 32)
+
+ if scale_factor != 1.0:
+ self.window = surface(width, height, True)
+ else:
+ self.window = self.screen
+
+ renpy.display.pgrender.set_rgba_masks()
+
+ # Should we redraw the screen from scratch?
+ self.full_redraw = True
+
+ # The surface used to display fullscreen video.
+ self.fullscreen_surface = self.screen
+
+ # Reset this on a mode change.
+ self.mouse_location = None
+ self.mouse_backing = None
+ self.mouse_backing_pos = None
+ self.mouse_info = None
+
+ return True
+
+ # private
+ def show_mouse(self, pos, info):
+ """
+ Actually shows the mouse.
+ """
+
+ self.mouse_location = pos
+ self.mouse_info = info
+
+ mxo, myo, tex = info
+
+ mx, my = pos
+ mw, mh = tex.get_size()
+
+ bx = mx - mxo
+ by = my - myo
+
+ self.mouse_backing_pos = (bx, by)
+ self.mouse_backing = surface(mw, mh, False)
+ self.mouse_backing.blit(self.window, (0, 0), (bx, by, mw, mh))
+
+ self.screen.blit(tex, (bx, by))
+
+ return bx, by, mw, mh
+
+ # private
+ def hide_mouse(self):
+ """
+ Actually hides the mouse.
+ """
+
+ size = self.mouse_backing.get_size()
+ self.screen.blit(self.mouse_backing, self.mouse_backing_pos)
+
+ rv = self.mouse_backing_pos + size
+
+ self.mouse_backing = None
+ self.mouse_backing_pos = None
+ self.mouse_location = None
+
+ return rv
+
+ # private
+ def draw_mouse(self, show_mouse):
+ """
+ This draws the mouse to the screen, if necessary. It uses the
+ buffer to minimize the amount of the screen that needs to be
+ drawn, and only redraws if the mouse has actually been moved.
+ """
+
+ hardware, x, y, tex = renpy.game.interface.get_mouse_info()
+
+ if self.mouse_old_visible != hardware:
+ pygame.mouse.set_visible(hardware)
+ self.mouse_old_visible = hardware
+
+ # The rest of this is for the software mouse.
+
+ if self.suppressed_blit:
+ return [ ]
+
+ if not show_mouse:
+ tex = None
+
+ info = (x, y, tex)
+ pos = pygame.mouse.get_pos()
+
+ if (pos == self.mouse_location and tex and info == self.mouse_info):
+ return [ ]
+
+ updates = [ ]
+
+ if self.mouse_location:
+ updates.append(self.hide_mouse())
+
+ if tex and pos and renpy.game.interface.focused:
+ updates.append(self.show_mouse(pos, info))
+
+ return updates
+
+ def update_mouse(self):
+ """
+ Draws the mouse, and then updates the screen.
+ """
+
+ updates = self.draw_mouse(True)
+
+ if updates:
+ pygame.display.update(updates)
+
+ def mouse_event(self, ev):
+ x, y = getattr(ev, 'pos', pygame.mouse.get_pos())
+
+ x /= self.scale_factor
+ y /= self.scale_factor
+
+ return x, y
+
+ def get_mouse_pos(self):
+ x, y = pygame.mouse.get_pos()
+
+ x /= self.scale_factor
+ y /= self.scale_factor
+
+ return x, y
+
+
+ def screenshot(self, surftree, fullscreen_video):
+ """
+ Returns a pygame surface containing a screenshot.
+ """
+
+ return self.window
+
+ def should_redraw(self, needs_redraw, first_pass):
+ """
+ Uses the framerate to determine if we can and should redraw.
+ """
+
+ if not needs_redraw:
+ return False
+
+ framerate = renpy.config.framerate
+
+ if framerate is None:
+ return True
+
+ next_frame = self.next_frame
+ now = pygame.time.get_ticks()
+
+ frametime = 1000.0 / framerate
+
+ # Handle timer rollover.
+ if next_frame > now + frametime:
+ next_frame = now
+
+ # It's not yet time for the next frame.
+ if now < next_frame and not first_pass:
+ return False
+
+ # Otherwise, it is. Schedule the next frame.
+ # if next_frame + frametime < now:
+ next_frame = now + frametime
+ # else:
+ # next_frame += frametime
+
+ self.next_frame = next_frame
+
+ return True
+
+
+ def draw_screen(self, surftree, fullscreen_video):
+ """
+ Draws the screen.
+ """
+
+ if not fullscreen_video:
+
+ updates = [ ]
+
+ updates.extend(self.draw_mouse(False))
+
+ damage = do_draw_screen(surftree, self.full_redraw, self)
+
+ if damage:
+ updates.extend(damage)
+
+ self.full_redraw = False
+
+ if self.window is self.screen:
+
+ updates.extend(self.draw_mouse(True))
+ pygame.display.update(updates)
+
+ else:
+
+ if self.scale_fast:
+ pygame.transform.scale(self.window, self.screen.get_size(), self.screen)
+ else:
+ renpy.display.scale.smoothscale(self.window, self.screen.get_size(), self.screen)
+
+ self.draw_mouse(True)
+ pygame.display.flip()
+
+ else:
+ pygame.display.flip()
+ self.full_redraw = True
+
+ self.suppressed_blit = fullscreen_video
+
+
+ def render_to_texture(self, render, alpha):
+
+ rv = surface(render.width, render.height, alpha)
+ draw(rv, None, render, 0, 0, False)
+
+ return rv
+
+ def is_pixel_opaque(self, what, x, y):
+
+ if x < 0 or y < 0 or x >= what.width or y >= what.height:
+ return 0
+
+ for (child, xo, yo, _focus, _main) in what.visible_children:
+ cx = x - xo
+ cy = y - yo
+
+ if what.forward:
+ cx, cy = what.forward.transform(cx, cy)
+
+
+ if isinstance(child, renpy.display.render.Render):
+ if self.is_pixel_opaque(child, x, y):
+ return True
+
+ else:
+ cx = int(cx)
+ cy = int(cy)
+
+ cw, ch = child.get_size()
+ if cx >= cw or cy >= ch:
+ return False
+
+
+
+ if not child.get_masks()[3] or child.get_at((cx, cy))[3]:
+ return True
+
+ return False
+
+
+ def mutated_surface(self, surf):
+ """
+ Called to indicate that the given surface has changed.
+ """
+
+ for i in clippers:
+ i.mutated.add(id(surf))
+
+ if surf in rle_cache:
+ del rle_cache[surf]
+
+
+ def load_texture(self, surf, transient=False):
+ """
+ Creates a texture from the surface. In the software implementation,
+ the only difference between a texture and a surface is that a texture
+ is in the RLE cache.
+ """
+
+ surf = copy_surface(surf)
+ self.mutated_surface(surf)
+
+ if transient:
+ return surf
+
+ if renpy.game.less_memory:
+ return surf
+
+ if surf not in rle_cache:
+ rle_surf = copy_surface(surf)
+ rle_surf.set_alpha(255, pygame.RLEACCEL)
+ self.mutated_surface(rle_surf)
+
+ rle_cache[surf] = rle_surf
+
+ return surf
+
+ def solid_texture(self, w, h, color):
+ """
+ Creates a texture filled to the edges with color.
+ """
+
+ surf = surface(w + 4, h + 4, True)
+ surf.fill(color)
+ self.mutated_surface(surf)
+
+ surf = surf.subsurface((2, 2, w, h))
+
+ self.mutated_surface(surf)
+ return surf
+
+
+ def free_memory(self):
+ """
+ Frees up memory.
+ """
+
+ rle_cache.clear()
+
+ def deinit(self):
+ """
+ Called when we're restarted.
+ """
+
+ renpy.display.render.free_memory()
+
+ return
+
+ def quit(self): #@ReservedAssignment
+ """
+ Shuts down the drawing system.
+ """
+
+ pygame.display.quit()
+
+ return
+
+ def event_peek_sleep(self):
+ """
+ Wait a little bit so the CPU doesn't speed up.
+ """
+
+ time.sleep(.0001)
+
+ def get_physical_size(self):
+ """
+ Return the physical width and height of the screen.
+ """
+ return renpy.config.screen_width, renpy.config.screen_height