def execute(self, state, t): self.report() if renpy.display.interface.trans_pause: return state if self.pattern: f = renpy.test.testfocus.find_focus(self.pattern) if f is None: return state else: f = None if state is True: points = renpy.python.py_eval(self.points) points = [renpy.test.testfocus.find_position(f, i) for i in points] if len(points) < 2: raise Exception("A drag requires at least two points.") interpoints = [] xa, ya = points[0] interpoints.append((xa, ya)) for xb, yb in points[1:]: for i in range(1, self.steps + 1): done = 1.0 * i / self.steps interpoints.append(( int(xa + done * (xb - xa)), int(ya + done * (yb - ya)), )) xa = xb ya = yb x, y = interpoints.pop(0) renpy.test.testmouse.move_mouse(x, y) renpy.test.testmouse.press_mouse(self.button) else: interpoints = state x, y = interpoints.pop(0) renpy.test.testmouse.move_mouse(x, y) if not interpoints: renpy.test.testmouse.release_mouse(self.button) return None else: return interpoints
def event(self, ev, x, y, st): if self.hide_request: return None if not self.state.events: # type: ignore return children = self.children offsets = self.offsets if not offsets: return None for i in range(len(self.children) - 1, -1, -1): d = children[i] xo, yo = offsets[i] cx = x - xo cy = y - yo # Transform screen coordinates to child coordinates. cx, cy = self.forward.transform(cx, cy) rv = d.event(ev, cx, cy, st) if rv is not None: return rv return None
def per_interact(self): super(VPGrid, self).per_interact() exc = None delta = 0 if None not in (self.grid_cols, self.grid_rows): delta = (self.grid_cols * self.grid_rows) - len(self.children) if delta > 0: exc = Exception("VPGrid not completely full.") else: given = self.grid_cols or self.grid_rows if given: # ignore the case where one is 0 - cannot be underfull delta = given - (len(self.children) % given) # the number of aditional children needed to complete # within [1, given], `given` being all right if delta < given: exc = Exception( "VPGrid not completely full, needs a multiple of {} children." .format(given)) if exc is not None: allow_underfull = self.allow_underfull if allow_underfull is None: allow_underfull = renpy.config.allow_underfull_grids or renpy.config.allow_unfull_vpgrids if not allow_underfull: raise exc for _ in range(delta): self.add(renpy.display.layout.Null())
def colormatrix(src, dst, matrix): c = [matrix[0:5], matrix[5:10], matrix[10:15], matrix[15:20]] offs = byte_offset(src) o = [None] * 4 for i in range(0, 4): o[offs[i]] = i # type: ignore _renpy.colormatrix( src, dst, c[o[0]][o[0]], c[o[0]][o[1]], c[o[0]][o[2]], c[o[0]][o[3]], c[o[0]][4], # type: ignore c[o[1]][o[0]], c[o[1]][o[1]], c[o[1]][o[2]], c[o[1]][o[3]], c[o[1]][4], # type: ignore c[o[2]][o[0]], c[o[2]][o[1]], c[o[2]][o[2]], c[o[2]][o[3]], c[o[2]][4], # type: ignore c[o[3]][o[0]], c[o[3]][o[1]], c[o[3]][o[2]], c[o[3]][o[3]], c[o[3]][4]) # type: ignore
def glyphs(self, s): rv = [ ] if not s: return rv for c in s: g = textsupport.Glyph() # @UndefinedVariable g.character = ord(c) g.ascent = self.baseline g.line_spacing = self.height if not is_zerowidth(g.character): width = self.width.get(c, None) if width is None: raise Exception("Character {0!r} not found in image-based font.".format(c)) g.width = self.width[c] g.advance = self.advance[c] else: g.width = 0 g.advance = 0 rv.append(g) # Compute kerning. for i in range(len(s) - 1): kern = self.kerns.get(s[i] + s[i + 1], self.default_kern) rv[i].advance += kern return rv
def find_position(f, position): """ Returns the virtual position of a coordinate located within focus `f`. If position is (None, None) returns the current mouse position (if in the focus), or a random position. If `f` is None, returns a position relative to the screen as a whole. """ posx, posy = position # Avoid moving the mouse when unnecessary. if renpy.test.testmouse.mouse_pos is not None: x, y = renpy.test.testmouse.mouse_pos else: x = random.randrange(renpy.config.screen_width) y = random.randrange(renpy.config.screen_height) if f is None: return ( relative_position(x, posx, renpy.config.screen_width), relative_position(y, posy, renpy.config.screen_height), ) orig_f = f # Check for the default widget. if f.x is None: f = f.copy() f.x = 0 f.y = 0 f.w = renpy.config.screen_width f.h = renpy.config.screen_height x = relative_position(x, posx, f.w) + f.x y = relative_position(y, posy, f.h) + f.y for _i in range(100): x = int(x) y = int(y) nf = renpy.display.render.focus_at_point(x, y) if nf is None: if orig_f.x is None: return x, y else: if (nf.widget == f.widget) and (nf.arg == f.arg): return x, y x = random.randrange(f.x, f.x + f.w) y = random.randrange(f.y, f.y + f.h) else: print() raise Exception("Could not locate the displayable.")
def write_rpyc_header(self, f): """ Writes an empty version 2 .rpyc header to the open binary file `f`. """ f.write(RPYC2_HEADER) for _i in range(3): f.write(struct.pack("III", 0, 0, 0))
def __init__(self, old, new): # Pick out a pivot element near the center of the list. new_center = (len(new) - 1) // 2 new_pivot = new[new_center] # Find an element in the old list corresponding to the pivot. old_half = (len(old) - 1) // 2 for i in range(0, old_half + 1): if old[old_half - i] is new_pivot: old_center = old_half - i break if old[old_half + i] is new_pivot: old_center = old_half + i break else: # If we couldn't, give up. self.pre = old self.start = 0 self.end = 0 self.post = [] return # Figure out the position of the overlap in the center of the two lists. new_start = new_center new_end = new_center + 1 old_start = old_center old_end = old_center + 1 len_new = len(new) len_old = len(old) while new_start and old_start and (new[new_start - 1] is old[old_start - 1]): new_start -= 1 old_start -= 1 while (new_end < len_new) and (old_end < len_old) and (new[new_end] is old[old_end]): new_end += 1 old_end += 1 # Now that we have this, we can put together the object. self.pre = list.__getitem__(old, slice(0, old_start)) self.start = new_start self.end = new_end self.post = list.__getitem__(old, slice(old_end, len_old))
def cycle_saves(name, count): """ :doc: loadsave Rotates the first `count` saves beginning with `name`. For example, if the name is auto- and the count is 10, then auto-9 will be renamed to auto-10, auto-8 will be renamed to auto-9, and so on until auto-1 is renamed to auto-2. """ for i in range(count - 1, 0, -1): rename_save(name + str(i), name + str(i + 1))
def event(self, ev, x, y, st): for i in range(len(self.children) - 1, -1, -1): s = self.children[i] if s.events: rv = s.cache.child.event(ev, x - s.x, y - s.y, st - s.cache.st) if rv is not None: return rv if self.event_function is not None: return self.event_function(ev, x, y, st) else: return None
def complete(self, begin=False): """ Called after a node is finished executing, before a save begins, or right before a rollback is attempted. This may be called more than once between calls to begin, and should always be called after an update to the store but before a rollback occurs. `begin` Should be true if called from begin(). """ if self.force_checkpoint: self.checkpoint(hard=False) self.force_checkpoint = False # Update self.current.stores with the changes from each store. # Also updates .ever_been_changed. for name, sd in renpy.python.store_dicts.items(): delta = sd.get_changes(begin) if delta: self.current.stores[name], self.current.delta_ebc[name] = delta # Update the list of mutated objects and what we need to do to # restore them. for _i in range(4): del self.current.objects[:] try: for _k, v in self.mutated.items(): if v is None: continue (ref, clean) = v obj = ref() if obj is None: continue compressed = obj._compress(clean) self.current.objects.append((obj, compressed)) break except RuntimeError: # This can occur when self.mutated is changed as we're # iterating over it. pass
def analyze(): """ Analyze the FPL and prints a report. """ if not fpl: return if renpy.config.frames < 30: return start = fpl[0][0] for t, _, event, _ in fpl: if event == renpy.config.profile_to_event: end = t break else: return if ( end - start ) < renpy.config.profile_time and not renpy.display.interface.profile_once: return s = "\n" renpy.log.real_stdout.write(s) renpy.display.log.write(s) times = [fpl[0][0]] * DEPTH_LEVELS for t, depth, event, args in fpl: dt = [(1000000 * (t - it)) if i <= depth else 0 for i, it in enumerate(times)] s = "{: 7.0f} {: 7.0f} {: 7.0f} {: 7.0f} {}\n".format( dt[0], dt[1], dt[2], dt[3], event.format(*args).replace("%", "%%"), ) renpy.log.real_stdout.write(s) renpy.display.log.write(s) for i in range(depth, DEPTH_LEVELS): times[i] = t
def merge_names(self, old_stmts, new_stmts, used_names): old_stmts = collapse_stmts(old_stmts) new_stmts = collapse_stmts(new_stmts) old_info = [ i.diff_info() for i in old_stmts ] new_info = [ i.diff_info() for i in new_stmts ] sm = difflib.SequenceMatcher(None, old_info, new_info) for oldl, newl, count in sm.get_matching_blocks(): for i in range(count): old = old_stmts[oldl + i] new = new_stmts[newl + i] if (new.name is None) and (new.name not in used_names): new.name = old.name used_names.add(new.name)
def init(): """ Initialize gamepad support. """ if not renpy.game.preferences.pad_enabled: return try: pygame_sdl2.controller.init() load_mappings() except Exception: renpy.display.log.exception() if not renpy.display.interface.safe_mode: try: for i in range(pygame_sdl2.controller.get_count()): start(i) except Exception: renpy.display.log.exception()
def create(self, particles, st): def ranged(n): if isinstance(n, tuple): return random.uniform(n[0], n[1]) else: return n if (st == 0) and not particles and self.fast: rv = [] for _i in range(0, self.count): rv.append( SnowBlossomParticle(self.image, ranged(self.xspeed), ranged(self.yspeed), self.border, st, random.uniform(0, 100), fast=True, rotate=self.rotate)) return rv if particles is None or len(particles) < self.count: # Check to see if we have a particle ready to start. If not, # don't start it. if particles and st < self.starts[len(particles)]: return None return [ SnowBlossomParticle(self.image, ranged(self.xspeed), ranged(self.yspeed), self.border, st, random.uniform(0, 100), fast=False, rotate=self.rotate) ]
def init(self): self.starts = [ random.uniform(0, self.start) for _i in range(0, self.count) ] # W0201 self.starts.append(self.start) self.starts.sort()
def predict(self): """ Performs image prediction, calling the given callback with each images that we predict to be loaded, in the rough order that they will be potentially loaded. """ if not self.current: return if renpy.config.predict_statements_callback is None: return old_images = self.images # A worklist of (node, images, return_stack) tuples. nodes = [] # The set of nodes we've seen. (We only consider each node once.) seen = set() # Find the roots. for label in renpy.config.predict_statements_callback(self.current): if not renpy.game.script.has_label(label): continue node = renpy.game.script.lookup(label) if node in seen: continue nodes.append((node, self.images, self.return_stack)) seen.add(node) # Predict statements. for i in range(0, renpy.config.predict_statements): if i >= len(nodes): break node, images, return_stack = nodes[i] self.images = renpy.display.image.ShownImageInfo(images) self.predict_return_stack = return_stack try: for n in node.predict(): if n is None: continue if n not in seen: nodes.append( (n, self.images, self.predict_return_stack)) seen.add(n) except Exception: if renpy.config.debug_prediction: import traceback print("While predicting images.") traceback.print_exc() print() self.images = old_images self.predict_return_stack = None yield True yield False
def Filmstrip(image, framesize, gridsize, delay, frames=None, loop=True, **properties): """ This creates an animation from a single image. This image must consist of a grid of frames, with the number of columns and rows in the grid being taken from gridsize, and the size of each frame in the grid being taken from framesize. This takes frames and sticks them into an Animation, with the given delay between each frame. The frames are taken by going from left-to-right across the first row, left-to-right across the second row, and so on until all frames are consumed, or a specified number of frames are taken. @param image: The image that the frames must be taken from. @param framesize: A (width, height) tuple giving the size of each of the frames in the animation. @param gridsize: A (columns, rows) tuple giving the number of columns and rows in the grid. @param delay: The delay, in seconds, between frames. @param frames: The number of frames in this animation. If None, then this defaults to colums * rows frames, that is, taking every frame in the grid. @param loop: If True, loop at the end of the animation. If False, this performs the animation once, and then stops. Other keyword arguments are as for anim.SMAnimation. """ width, height = framesize cols, rows = gridsize if frames is None: frames = cols * rows i = 0 # Arguments to Animation args = [ ] for r in range(0, rows): for c in range(0, cols): x = c * width y = r * height args.append(renpy.display.im.Crop(image, x, y, width, height)) args.append(delay) i += 1 if i == frames: break if i == frames: break if not loop: args.pop() return Animation(*args, **properties)
def print_path(o, objs): prefix = "" seen = set() queue = [] objects = [] last = None for _i in range(30): objects.append(o) last = o print(prefix + "%x" % id(o), "(%d referrers)" % len(gc.get_referrers(o)), type(o), end=' ') try: if isinstance(o, dict) and "__name__" in o: print("with name", o["__name__"]) else: print(repr(o)) except Exception: print("Bad repr.") found = False if isinstance(o, types.ModuleType): if not queue: break o, prefix = queue.pop() continue if isinstance(o, weakref.WeakKeyDictionary): for k, v in o.items(): if v is objects[-4]: k = k() seen.add(id(k)) queue.append((k, prefix + " (key) ")) for i in gc.get_referrers(o): if i is objs or i is objects: continue if id(i) in seen: continue if isinstance(i, types.FrameType): continue seen.add(id(i)) queue.append((i, prefix + " ")) found = True break if not queue: break if not found: print("<no parent, popping>") o, prefix = queue.pop() for i in gc.get_referrers(last): print(prefix + "<- %x" % id(i), type(i)) del objects[:]
def add(self, sma): for _i in range(0, self.prob): sma.edges.setdefault(self.old, []).append(self)
def main(): gc.set_threshold(*renpy.config.gc_thresholds) log_clock("Bootstrap to the start of init.init") renpy.game.exception_info = 'Before loading the script.' # Clear the line cache, since the script may have changed. linecache.clearcache() # Get ready to accept new arguments. renpy.arguments.pre_init() # Init the screen language parser. renpy.sl2.slparser.init() # Init the config after load. renpy.config.init() # Reset live2d if it exists. try: renpy.gl2.live2d.reset() except: pass # Set up variants. choose_variants() renpy.display.touch = "touch" in renpy.config.variants log_clock("Early init") # Note the game directory. game.basepath = renpy.config.gamedir renpy.config.searchpath = [renpy.config.gamedir] # Find the common directory. commondir = __main__.path_to_common( renpy.config.renpy_base) # E1101 @UndefinedVariable if os.path.isdir(commondir): renpy.config.searchpath.append(commondir) renpy.config.commondir = commondir else: renpy.config.commondir = None # Add path from env variable, if any if "RENPY_SEARCHPATH" in os.environ: renpy.config.searchpath.extend( os.environ["RENPY_SEARCHPATH"].split("::")) if renpy.android: renpy.config.commondir = None android_searchpath() # Load Ren'Py extensions. for dir in renpy.config.searchpath: # @ReservedAssignment for fn in os.listdir(dir): if fn.lower().endswith(".rpe"): load_rpe(dir + "/" + fn) # Generate a list of extensions for each archive handler. archive_extensions = [] for handler in renpy.loader.archive_handlers: for ext in handler.get_supported_extensions(): if not (ext in archive_extensions): archive_extensions.append(ext) # Find archives. for dn in renpy.config.searchpath: if not os.path.isdir(dn): continue for i in sorted(os.listdir(dn)): base, ext = os.path.splitext(i) # Check if the archive does not have any of the extensions in archive_extensions if not (ext in archive_extensions): continue renpy.config.archives.append(base) renpy.config.archives.reverse() # Initialize archives. renpy.loader.index_archives() # Start auto-loading. renpy.loader.auto_init() log_clock("Loader init") # Initialize the log. game.log = renpy.python.RollbackLog() # Initialize the store. renpy.store.store = sys.modules['store'] # type: ignore # Set up styles. game.style = renpy.style.StyleManager() # @UndefinedVariable renpy.store.style = game.style # Run init code in its own context. (Don't log.) game.contexts = [renpy.execution.Context(False)] game.contexts[0].init_phase = True renpy.execution.not_infinite_loop(60) # Load the script. renpy.game.exception_info = 'While loading the script.' renpy.game.script = renpy.script.Script() if renpy.session.get("compile", False): renpy.game.args.compile = True # type: ignore # Set up error handling. renpy.exports.load_module("_errorhandling") if renpy.exports.loadable("tl/None/common.rpym") or renpy.exports.loadable( "tl/None/common.rpymc"): renpy.exports.load_module("tl/None/common") renpy.config.init_system_styles() renpy.style.build_styles() # @UndefinedVariable log_clock("Loading error handling") # If recompiling everything, remove orphan .rpyc files. # Otherwise, will fail in case orphan .rpyc have same # labels as in other scripts (usually happens on script rename). if (renpy.game.args.command == 'compile' ) and not (renpy.game.args.keep_orphan_rpyc): # type: ignore for (fn, dn) in renpy.game.script.script_files: if dn is None: continue if not os.path.isfile(os.path.join(dn, fn + ".rpy")): try: name = os.path.join(dn, fn + ".rpyc") os.rename(name, name + ".bak") except OSError: # This perhaps shouldn't happen since either .rpy or .rpyc should exist pass # Update script files list, so that it doesn't contain removed .rpyc's renpy.loader.cleardirfiles() renpy.game.script.scan_script_files() # Load all .rpy files. renpy.game.script.load_script() # sets renpy.game.script. log_clock("Loading script") if renpy.game.args.command == 'load-test': # type: ignore start = time.time() for i in range(5): print(i) renpy.game.script = renpy.script.Script() renpy.game.script.load_script() print(time.time() - start) sys.exit(0) renpy.game.exception_info = 'After loading the script.' # Find the save directory. if renpy.config.savedir is None: renpy.config.savedir = __main__.path_to_saves( renpy.config.gamedir) # E1101 @UndefinedVariable if renpy.game.args.savedir: # type: ignore renpy.config.savedir = renpy.game.args.savedir # type: ignore # Init preferences. game.persistent = renpy.persistent.init() game.preferences = game.persistent._preferences for i in renpy.game.persistent._seen_translates: # type: ignore if i in renpy.game.script.translator.default_translates: renpy.game.seen_translates_count += 1 if game.persistent._virtual_size: renpy.config.screen_width, renpy.config.screen_height = game.persistent._virtual_size # Init save locations and loadsave. renpy.savelocation.init() # We need to be 100% sure we kill the savelocation thread. try: # Init save slots. renpy.loadsave.init() log_clock("Loading save slot metadata.") # Load persistent data from all save locations. renpy.persistent.update() game.preferences = game.persistent._preferences log_clock("Loading persistent") # Clear the list of seen statements in this game. game.seen_session = {} # Initialize persistent variables. renpy.store.persistent = game.persistent # type: ignore renpy.store._preferences = game.preferences # type: ignore renpy.store._test = renpy.test.testast._test # type: ignore if renpy.parser.report_parse_errors(): raise renpy.game.ParseErrorException() renpy.game.exception_info = 'While executing init code:' for _prio, node in game.script.initcode: if isinstance(node, renpy.ast.Node): node_start = time.time() renpy.game.context().run(node) node_duration = time.time() - node_start if node_duration > renpy.config.profile_init: renpy.display.log.write(" - Init at %s:%d took %.5f s.", node.filename, node.linenumber, node_duration) else: # An init function. node() renpy.game.exception_info = 'After initialization, but before game start.' # Check if we should simulate android. renpy.android = renpy.android or renpy.config.simulate_android # @UndefinedVariable # Re-set up the logging. renpy.log.post_init() # Run the post init code, if any. for i in renpy.game.post_init: i() renpy.game.script.report_duplicate_labels() # Sort the images. renpy.display.image.image_names.sort() game.persistent._virtual_size = renpy.config.screen_width, renpy.config.screen_height # type: ignore log_clock("Running init code") renpy.pyanalysis.load_cache() log_clock("Loading analysis data") # Analyze the script and compile ATL. renpy.game.script.analyze() renpy.atl.compile_all() log_clock("Analyze and compile ATL") # Index the archive files. We should not have loaded an image # before this point. (As pygame will not have been initialized.) # We need to do this again because the list of known archives # may have changed. renpy.loader.index_archives() log_clock("Index archives") # Check some environment variables. renpy.game.less_memory = "RENPY_LESS_MEMORY" in os.environ renpy.game.less_mouse = "RENPY_LESS_MOUSE" in os.environ renpy.game.less_updates = "RENPY_LESS_UPDATES" in os.environ renpy.dump.dump(False) renpy.game.script.make_backups() log_clock("Dump and make backups.") # Initialize image cache. renpy.display.im.cache.init() log_clock("Cleaning cache") # Make a clean copy of the store. renpy.python.make_clean_stores() log_clock("Making clean stores") gc.collect(2) if gc.garbage: del gc.garbage[:] if renpy.config.manage_gc: gc.set_threshold(*renpy.config.gc_thresholds) gc_debug = int(os.environ.get("RENPY_GC_DEBUG", 0)) if renpy.config.gc_print_unreachable: gc_debug |= gc.DEBUG_SAVEALL gc.set_debug(gc_debug) else: gc.set_threshold(700, 10, 10) log_clock("Initial gc.") # Start debugging file opens. renpy.debug.init_main_thread_open() # (Perhaps) Initialize graphics. if not game.interface: renpy.display.core.Interface() log_clock("Creating interface object") # Start things running. restart = None while True: if restart: renpy.display.screen.before_restart() try: try: run(restart) finally: restart = (renpy.config.end_game_transition, "_invoke_main_menu", "_main_menu") renpy.persistent.update(True) renpy.persistent.save_MP() except game.FullRestartException as e: restart = e.reason finally: # Reset live2d if it exists. try: renpy.gl2.live2d.reset_states() except: pass # Flush any pending interface work. renpy.display.interface.finish_pending() # Give Ren'Py a couple of seconds to finish saving. renpy.loadsave.autosave_not_running.wait(3.0) finally: gc.set_debug(0) for i in renpy.config.quit_callbacks: i() renpy.loader.auto_quit() renpy.savelocation.quit() renpy.translation.write_updated_strings() # This is stuff we do on a normal, non-error return. if not renpy.display.error.error_handled: renpy.display.render.check_at_shutdown()
def draw(x0, x1, y0, y1): # Compute the coordinates of the left, right, top, and # bottom sides of the region, for both the source and # destination surfaces. # left side. if x0 >= 0: dx0 = x0 sx0 = x0 else: dx0 = dw + x0 sx0 = sw + x0 # right side. if x1 > 0: dx1 = x1 sx1 = x1 else: dx1 = dw + x1 sx1 = sw + x1 # top side. if y0 >= 0: dy0 = y0 sy0 = y0 else: dy0 = dh + y0 sy0 = sh + y0 # bottom side if y1 > 0: dy1 = y1 sy1 = y1 else: dy1 = dh + y1 sy1 = sh + y1 # Quick exit. if sx0 == sx1 or sy0 == sy1 or dx1 <= dx0 or dy1 <= dy0: return # Compute sizes. srcsize = (sx1 - sx0, sy1 - sy0) dstsize = (int(dx1 - dx0), int(dy1 - dy0)) # Get a subsurface. surf = source.subsurface((sx0, sy0, srcsize[0], srcsize[1])) # Scale or tile if we have to. if dstsize != srcsize: if self.tile: tilew, tileh = srcsize dstw, dsth = dstsize xtiles = max(1, dstw // tilew + (1 if dstw % tilew else 0)) ytiles = max(1, dsth // tileh + (1 if dsth % tileh else 0)) if dstw % tilew or dsth % tileh: # Area is not an exact integer number of tiles if self.tile == "integer": if dstw % tilew / float(tilew) < self.tile_ratio: xtiles = max(1, xtiles - 1) if dsth % tileh / float(tileh) < self.tile_ratio: ytiles = max(1, ytiles - 1) # Tile at least one tile in each direction surf2 = renpy.display.pgrender.surface_unscaled( (tilew * xtiles, tileh * ytiles), surf) for y in range(0, ytiles): for x in range(0, xtiles): surf2.blit(surf, (x * tilew, y * tileh)) if self.tile is True: # Trim the tiled surface to required size surf = surf2.subsurface((0, 0, dstw, dsth)) else: # Using integer full 'tiles' per side srcsize = (tilew * xtiles, tileh * ytiles) surf = surf2 if dstsize != srcsize: surf2 = renpy.display.scale.real_transform_scale( surf, dstsize) surf = surf2 # Blit. dest.blit(surf, (dx0, dy0))
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 = b"\x00" * 256 for i in range(0, ramplen): ramp += bchr(255 * i // ramplen) ramp += b"\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(dest.get_masks()[3]) px = what.operation_parameter renpy.display.module.pixellate( surf.subsurface((-x, -y, w, h)), dest.subsurface((0, 0, w, h)), px, px, px, px) elif what.operation == FLATTEN: surf = what.children[0][0].render_to_texture(dest.get_masks()[3]) dest.subsurface((0, 0, w, h)).blit(surf, (0, 0)) else: raise Exception("Unknown operation: %d" % what.operation)
def add(self, font, start, end, target=None, target_increment=False): """ :doc: font_group Associates a range of characters with a `font`. `start` The start of the range. This may be a single-character string, or an integer giving a unicode code point. If start is None, then the font is used as the default. `end` The end of the range. This may be a single-character string, or an integer giving a unicode code point. This is ignored if start is None. `target` If given, associates the given range of characters with specific characters from the given font, depending on target_increment. This may be a single-character string, or an integer giving a unicode code point. This is ignored if the character had already been added. `target_increment` If True, the [start, end] range is mapped to the [target, target+end-start] range. If False, every character from the range is associated with the target character. When multiple .add() calls include the same character, the first call takes precedence. This returns the FontGroup, so that multiple calls to .add() can be chained together. """ if start is None: if isinstance(font, FontGroup): for k, v in font.map.items(): if k not in self.map: self.map[k] = v else: if None not in self.map: self.map[None] = font return self if not isinstance(start, int): start = ord(start) if not isinstance(end, int): end = ord(end) if target and not isinstance(target, int): target = ord(target) if end < start: raise Exception( "In FontGroup.add, the start of a character range must be before the end of the range." ) for i in range(start, end + 1): if i not in self.map: self.map[i] = font if target is not None: self.char_map[i] = target if target_increment: target += 1 return self
def draw(x0, x1, y0, y1): # Compute the coordinates of the left, right, top, and # bottom sides of the region, for both the source and # destination surfaces. # left side. if x0 >= 0: dx0 = x0 sx0 = x0 else: dx0 = dw + x0 sx0 = sw + x0 # right side. if x1 > 0: dx1 = x1 sx1 = x1 else: dx1 = dw + x1 sx1 = sw + x1 # top side. if y0 >= 0: dy0 = y0 sy0 = y0 else: dy0 = dh + y0 sy0 = sh + y0 # bottom side if y1 > 0: dy1 = y1 sy1 = y1 else: dy1 = dh + y1 sy1 = sh + y1 # Quick exit. if sx0 == sx1 or sy0 == sy1: return # Compute sizes. csw = sx1 - sx0 csh = sy1 - sy0 cdw = dx1 - dx0 cdh = dy1 - dy0 if csw <= 0 or csh <= 0 or cdh <= 0 or cdw <= 0: return # Get a subsurface. cr = crend.subsurface((sx0, sy0, csw, csh)) # Scale or tile if we have to. if csw != cdw or csh != cdh: if self.tile: ctw, cth = cdw, cdh xtiles = max(1, cdw // csw + (1 if cdw % csw else 0)) ytiles = max(1, cdh // csh + (1 if cdh % csh else 0)) if cdw % csw or cdh % csh: # Area is not an exact integer number of tiles if self.tile == "integer": if cdw % csw / float(csw) < self.tile_ratio: xtiles = max(1, xtiles - 1) if cdh % csh / float(csh) < self.tile_ratio: ytiles = max(1, ytiles - 1) # Set size of the used tiles (ready to scale) ctw, cth = csw * xtiles, csh * ytiles newcr = Render(ctw, cth) newcr.xclipping = True newcr.yclipping = True for x in range(0, xtiles): for y in range(0, ytiles): newcr.blit(cr, (x * csw, y * csh)) csw, csh = ctw, cth cr = newcr if csw != cdw or csh != cdh: # Subsurface needs scaling newcr = Render(cdw, cdh) newcr.forward = Matrix2D(1.0 * csw / cdw, 0, 0, 1.0 * csh / cdh) newcr.reverse = Matrix2D(1.0 * cdw / csw, 0, 0, 1.0 * cdh / csh) newcr.blit(cr, (0, 0)) cr = newcr # Blit. rv.blit(cr, (dx0, dy0)) return
def revertable_range(*args): return RevertableList(range(*args))