Compare commits
112 Commits
6.15.0.263
...
6.16
| Author | SHA1 | Date | |
|---|---|---|---|
| c12adecf9f | |||
| fc6013c5de | |||
| 3dd9f07fe2 | |||
| 5308fdd0b8 | |||
| 273d1e296c | |||
| 152af7dc7b | |||
| 43b34f22a3 | |||
| 3330b451d0 | |||
| 5290296952 | |||
| 8fc784aef5 | |||
| 110c8b890b | |||
| b6c686d770 | |||
| 6358db88ff | |||
| b2611fd613 | |||
| 5eb3af0188 | |||
| ad71c17f77 | |||
| 0d7dec8e4e | |||
| 112fbbfc08 | |||
| 4ee5240c72 | |||
| 025e359394 | |||
| 143bcfad08 | |||
| a39ebd13e0 | |||
| e7c0a5f19f | |||
| f0b03dc426 | |||
| 3d2ad79b6b | |||
| 9f28a93c56 | |||
| 386dd1d799 | |||
| e85ebf3a50 | |||
| 81eb2e6f42 | |||
| 9d021a2ec1 | |||
| 439fac65e2 | |||
| dee484a16b | |||
| 90457d6f20 | |||
| 245c30d7a7 | |||
| 81b28293ca | |||
| 392f71b214 | |||
| 257a5281ed | |||
| d44f70a145 | |||
| ec9198f25d | |||
| 1ffd4f97fc | |||
| e8968252ab | |||
| ae618ddd76 | |||
| 005aa1a64c | |||
| a63231d36f | |||
| db530f7def | |||
| 627261f51d | |||
| d080b71f29 | |||
| 0a62195625 | |||
| 571bb43459 | |||
| 817b9d5c9f | |||
| 6f3fa9bf8d | |||
| 2de5a7d9f7 | |||
| d26f1a65e8 | |||
| 667c2e59d3 | |||
| b4411a027f | |||
| 142831ec44 | |||
| 2dd58759de | |||
| 866f3a11b2 | |||
| 8a2d779320 | |||
| 2ef8cd4c3f | |||
| 88ec1e66e6 | |||
| f6bfeefc8e | |||
| bbdf9023fe | |||
| b0070122f8 | |||
| ad51bfb30f | |||
| d15ae51825 | |||
| 2efa265983 | |||
| cc6513c93a | |||
| a81865484c | |||
| d70eb13cb0 | |||
| 4b5d94eeb9 | |||
| 5674a4ddac | |||
| 5bb6483ab4 | |||
| f62e4b6eb7 | |||
| c54a613393 | |||
| 2c4be00267 | |||
| 0b8c22e0d8 | |||
| 65a7dca95f | |||
| fdcfaface0 | |||
| 5f08884b52 | |||
| 3595eee11c | |||
| b33eb54c34 | |||
| c86fc743bc | |||
| 10b82603b9 | |||
| 1a1ae36299 | |||
| cc004c00f0 | |||
| cda96afd14 | |||
| 966630ea16 | |||
| 8c746bdccc | |||
| 4fac7da3bb | |||
| 4045958cd2 | |||
| 45155a9ff4 | |||
| 83ad789654 | |||
| 21eaa5ee9c | |||
| 69bcd84d5d | |||
| 7357fa4c5b | |||
| d51c768112 | |||
| 32ae4fef3a | |||
| fd5ee77f1c | |||
| b9acbfccd7 | |||
| d05059e64d | |||
| bc80e38bb5 | |||
| 838233a68f | |||
| 6968d973a6 | |||
| 199f6aa70c | |||
| b7e33bb2b9 | |||
| d2d777be0c | |||
| 4c924b4971 | |||
| ddd4a1a4ea | |||
| c68f240061 | |||
| 5d9d4955ff | |||
| fb4eeaaaeb |
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from renpy import version_tuple #@UnresolvedImport
|
||||
|
||||
version = ".".join(str(i) for i in version_tuple)
|
||||
short_version = ".".join(str(i) for i in version_tuple[:3])
|
||||
print "Version", version
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
|
||||
ap.add_argument("--release", action="store_true")
|
||||
ap.add_argument("--prerelease", action="store_true")
|
||||
ap.add_argument("--experimental", action="store_true")
|
||||
ap.add_argument("--no-tag", "-n", action="store_true")
|
||||
|
||||
args = ap.parse_args()
|
||||
|
||||
if args.release:
|
||||
links = [ "release", "prerelease", "experimental" ]
|
||||
tag = True
|
||||
elif args.prerelease:
|
||||
links = [ "prerelease", "experimental" ]
|
||||
tag = True
|
||||
elif args.experimental:
|
||||
links = [ "experimental" ]
|
||||
tag = False
|
||||
else:
|
||||
links = [ ]
|
||||
tag = False
|
||||
|
||||
os.chdir("/home/tom/ab/renpy/dl")
|
||||
|
||||
for i in links:
|
||||
if os.path.exists(i):
|
||||
os.unlink(i)
|
||||
os.symlink(short_version, i)
|
||||
|
||||
os.chdir("/home/tom/ab/renpy")
|
||||
|
||||
if tag and not args.no_tag:
|
||||
cmd = [ "git", "tag", "-a", version, "-m", "Ren'Py " + version ]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
os.chdir("/home/tom/ab/website")
|
||||
subprocess.check_call("./upload.sh")
|
||||
@@ -112,7 +112,7 @@ display: none;
|
||||
<dd>Toggles fullscreen mode</dd>
|
||||
<dt>S</dt>
|
||||
<dd>Takes a screenshot, saving it in a file named screenshotxxxx.png, where xxxx is a serial number.</dd>
|
||||
<dt>Alt-H, Command-H</dt>
|
||||
<dt>Alt-M, Command-H</dt>
|
||||
<dd>Hides (iconifies) the window.</dd>
|
||||
<dt>Alt-F4, Command-Q</dt>
|
||||
<dd>Quits the game.</dd>
|
||||
|
||||
@@ -4,7 +4,7 @@ label add_file:
|
||||
import os
|
||||
import codecs
|
||||
|
||||
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash")
|
||||
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash", cancel=Jump("navigation"))
|
||||
|
||||
if "." in filename and not filename.endswith(".rpy"):
|
||||
interface.error(_("The filename must have the .rpy extension."), label="navigation")
|
||||
|
||||
@@ -47,7 +47,7 @@ init python in distribute:
|
||||
regexp += r'.*'
|
||||
pattern = pattern[2:]
|
||||
elif pattern[0] == "*":
|
||||
regexp += r'[^/]*'
|
||||
regexp += r'[^/]*/?'
|
||||
pattern = pattern[1:]
|
||||
elif pattern[0] == '[':
|
||||
regexp += r'['
|
||||
@@ -152,6 +152,26 @@ init python in distribute:
|
||||
|
||||
return rv
|
||||
|
||||
def filter_empty(self):
|
||||
"""
|
||||
Makes a deep copy of this file list with empty directories
|
||||
omitted.
|
||||
"""
|
||||
|
||||
rv = FileList()
|
||||
|
||||
needed_dirs = set()
|
||||
|
||||
for i in reversed(self):
|
||||
|
||||
if (not i.directory) or (i.name in needed_dirs):
|
||||
rv.insert(0, i.copy())
|
||||
|
||||
directory, _sep, _filename = i.name.rpartition("/")
|
||||
needed_dirs.add(directory)
|
||||
|
||||
return rv
|
||||
|
||||
@staticmethod
|
||||
def merge(l):
|
||||
"""
|
||||
@@ -675,6 +695,9 @@ init python in distribute:
|
||||
fl = fl.copy()
|
||||
fl.sort()
|
||||
|
||||
if self.build.get("exclude_empty_directories", True):
|
||||
fl = fl.filter_empty()
|
||||
|
||||
# Write the update information.
|
||||
update_files = [ ]
|
||||
update_xbit = [ ]
|
||||
|
||||
@@ -174,7 +174,14 @@ init -1 python hide:
|
||||
|
||||
_game_menu_screen = None
|
||||
|
||||
config.underlay = [ ]
|
||||
config.underlay = [
|
||||
renpy.Keymap(
|
||||
quit = renpy.quit_event,
|
||||
iconify = renpy.iconify,
|
||||
choose_renderer = renpy.curried_call_in_new_context("_choose_renderer"),
|
||||
),
|
||||
]
|
||||
|
||||
config.rollback_enabled = False
|
||||
|
||||
|
||||
|
||||
@@ -7,16 +7,88 @@ init python in distribute:
|
||||
import zipfile
|
||||
import tarfile
|
||||
import zlib
|
||||
import struct
|
||||
import stat
|
||||
|
||||
from zipfile import crc32
|
||||
|
||||
zlib.Z_DEFAULT_COMPRESSION = 9
|
||||
|
||||
class ZipFile(zipfile.ZipFile):
|
||||
|
||||
def write_with_info(self, zinfo, filename):
|
||||
"""Put the bytes from filename into the archive under the name
|
||||
arcname."""
|
||||
if not self.fp:
|
||||
raise RuntimeError(
|
||||
"Attempt to write to ZIP archive that was already closed")
|
||||
|
||||
st = os.stat(filename)
|
||||
isdir = stat.S_ISDIR(st.st_mode)
|
||||
|
||||
zinfo.file_size = st.st_size
|
||||
zinfo.flag_bits = 0x00
|
||||
zinfo.header_offset = self.fp.tell() # Start of header bytes
|
||||
|
||||
self._writecheck(zinfo)
|
||||
self._didModify = True
|
||||
|
||||
if isdir:
|
||||
zinfo.file_size = 0
|
||||
zinfo.compress_size = 0
|
||||
zinfo.CRC = 0
|
||||
self.filelist.append(zinfo)
|
||||
self.NameToInfo[zinfo.filename] = zinfo
|
||||
self.fp.write(zinfo.FileHeader())
|
||||
return
|
||||
|
||||
with open(filename, "rb") as fp:
|
||||
# Must overwrite CRC and sizes with correct data later
|
||||
zinfo.CRC = CRC = 0
|
||||
zinfo.compress_size = compress_size = 0
|
||||
zinfo.file_size = file_size = 0
|
||||
self.fp.write(zinfo.FileHeader())
|
||||
if zinfo.compress_type == zipfile.ZIP_DEFLATED:
|
||||
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
|
||||
zlib.DEFLATED, -15)
|
||||
else:
|
||||
cmpr = None
|
||||
while 1:
|
||||
buf = fp.read(1024 * 1024)
|
||||
if not buf:
|
||||
break
|
||||
file_size = file_size + len(buf)
|
||||
CRC = crc32(buf, CRC) & 0xffffffff
|
||||
if cmpr:
|
||||
buf = cmpr.compress(buf)
|
||||
compress_size = compress_size + len(buf)
|
||||
self.fp.write(buf)
|
||||
if cmpr:
|
||||
buf = cmpr.flush()
|
||||
compress_size = compress_size + len(buf)
|
||||
self.fp.write(buf)
|
||||
zinfo.compress_size = compress_size
|
||||
else:
|
||||
zinfo.compress_size = file_size
|
||||
zinfo.CRC = CRC
|
||||
zinfo.file_size = file_size
|
||||
# Seek backwards and write CRC and file sizes
|
||||
position = self.fp.tell() # Preserve current position in file
|
||||
self.fp.seek(zinfo.header_offset + 14, 0)
|
||||
self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
|
||||
zinfo.file_size))
|
||||
self.fp.seek(position, 0)
|
||||
self.filelist.append(zinfo)
|
||||
self.NameToInfo[zinfo.filename] = zinfo
|
||||
|
||||
|
||||
class ZipPackage(object):
|
||||
"""
|
||||
A class that creates a zip file.
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
self.zipfile = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
|
||||
self.zipfile = ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
|
||||
|
||||
def add_file(self, name, path, xbit):
|
||||
|
||||
@@ -31,18 +103,26 @@ init python in distribute:
|
||||
zi.create_system = 3
|
||||
|
||||
if xbit:
|
||||
zi.external_attr = long(0100777) << 16
|
||||
zi.external_attr = long(0100755) << 16
|
||||
else:
|
||||
zi.external_attr = long(0100666) << 16
|
||||
zi.external_attr = long(0100644) << 16
|
||||
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
self.zipfile.writestr(zi, data)
|
||||
self.zipfile.write_with_info(zi, path)
|
||||
|
||||
def add_directory(self, name, path):
|
||||
return
|
||||
|
||||
if path is None:
|
||||
return
|
||||
|
||||
zi = zipfile.ZipInfo(name + "/")
|
||||
|
||||
s = os.stat(path)
|
||||
zi.date_time = time.gmtime(s.st_mtime)[:6]
|
||||
zi.compress_type = zipfile.ZIP_STORED
|
||||
zi.create_system = 3
|
||||
zi.external_attr = (long(0040755) << 16) | 0x10
|
||||
|
||||
self.zipfile.write_with_info(zi, path)
|
||||
|
||||
def close(self):
|
||||
self.zipfile.close()
|
||||
|
||||
@@ -70,9 +150,9 @@ init python in distribute:
|
||||
info.type = tarfile.DIRTYPE
|
||||
|
||||
if xbit:
|
||||
info.mode = 0777
|
||||
info.mode = 0755
|
||||
else:
|
||||
info.mode = 0666
|
||||
info.mode = 0644
|
||||
|
||||
info.uid = 1000
|
||||
info.gid = 1000
|
||||
|
||||
+13
-24
@@ -5,13 +5,10 @@
|
||||
|
||||
init python:
|
||||
try:
|
||||
import EasyDialogs
|
||||
except ImportError:
|
||||
try:
|
||||
import EasyDialogsWin as EasyDialogs
|
||||
except:
|
||||
EasyDialogs = None
|
||||
|
||||
import EasyDialogsWin as EasyDialogs
|
||||
except:
|
||||
EasyDialogs = None
|
||||
|
||||
import os
|
||||
|
||||
init python in project:
|
||||
@@ -485,16 +482,13 @@ label choose_projects_directory:
|
||||
|
||||
python hide:
|
||||
|
||||
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory."), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."))
|
||||
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory using the directory chooser.\n{b}The directory chooser may have opened behind this window.{/b}"), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."),)
|
||||
|
||||
path = persistent.projects_directory
|
||||
|
||||
if path:
|
||||
|
||||
default_path = path
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
|
||||
except:
|
||||
@@ -513,27 +507,22 @@ label choose_projects_directory:
|
||||
else:
|
||||
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
|
||||
if 'RENPY_ORIGINAL_LD_LIBRARY_PATH' in env:
|
||||
env['LD_LIBRARY_PATH'] = env['RENPY_ORIGINAL_LD_LIBRARY_PATH']
|
||||
|
||||
cmd = [ "zenity", "--title=Select Projects Directory", "--file-selection", "--directory", "--filename=" + renpy.fsencode(default_path) ]
|
||||
|
||||
zen = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE)
|
||||
|
||||
choice = zen.stdout.read()
|
||||
zen.wait()
|
||||
|
||||
cmd = [ "/usr/bin/python", os.path.join(config.gamedir, "tkaskdir.py"), renpy.fsencode(default_path) ]
|
||||
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
choice = p.stdout.read()
|
||||
p.wait()
|
||||
|
||||
if choice:
|
||||
path = renpy.fsdecode(choice[:-1])
|
||||
path = renpy.fsdecode(choice)
|
||||
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
path = None
|
||||
interface.error(_("Ren'Py was unable to run zenity to choose the projects directory."), label=None)
|
||||
interface.error(_("Ren'Py was unable to run python with tkinter to choose the projects directory."), label=None)
|
||||
|
||||
if path is None:
|
||||
path = default_path
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
init -999:
|
||||
$ config.script_version = (6, 15, 0)
|
||||
$ config.script_version = (6, 15, 7)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
|
||||
# See LICENSE.txt for license details.
|
||||
|
||||
# This is used on Linux and Mac to prompt the user for the projects
|
||||
# directory.
|
||||
|
||||
import sys
|
||||
|
||||
# Python3 and Python2-style imports.
|
||||
try:
|
||||
from tkinter import Tk
|
||||
from tkinter.filedialog import askdirectory
|
||||
except ImportError:
|
||||
from Tkinter import Tk
|
||||
from tkFileDialog import askdirectory
|
||||
|
||||
# Create the TK canvas.
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
|
||||
result = askdirectory(initialdir=sys.argv[1], parent=root, title="Select Ren'Py Projects Directory")
|
||||
sys.stdout.write(result)
|
||||
@@ -7,7 +7,7 @@ label translate:
|
||||
|
||||
python:
|
||||
|
||||
language = interface.input(_("Create or Update Translations"), _("Please enter the name of the language for which you want to create or update translations."), filename=True, default=persistent.translate_language)
|
||||
language = interface.input(_("Create or Update Translations"), _("Please enter the name of the language for which you want to create or update translations."), filename=True, default=persistent.translate_language, cancel=Jump("front_page"))
|
||||
|
||||
language = language.strip()
|
||||
|
||||
|
||||
+8
-3
@@ -23,13 +23,16 @@
|
||||
def version():
|
||||
return (6, 12, 0)
|
||||
|
||||
cdef extern from "renpy.h":
|
||||
|
||||
cdef extern from "pygame/pygame.h":
|
||||
cdef struct SDL_RWops:
|
||||
pass
|
||||
|
||||
void import_pygame_rwobject()
|
||||
SDL_RWops* RWopsFromPython(object obj)
|
||||
|
||||
|
||||
cdef extern from "renpy.h":
|
||||
|
||||
void core_init()
|
||||
|
||||
void save_png_core(object, SDL_RWops *, int)
|
||||
@@ -421,5 +424,7 @@ def subpixel(pysrc, pydst, xoffset, yoffset, shift):
|
||||
|
||||
|
||||
# Be sure to update scale.py when adding something new here!
|
||||
|
||||
|
||||
import_pygame_rwobject()
|
||||
core_init()
|
||||
|
||||
|
||||
+237
-28
@@ -25,12 +25,12 @@
|
||||
#include <libavutil/avstring.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
// #include "libavdevice/avdevice.h"
|
||||
#include <libswscale/swscale.h>
|
||||
// #include "libavcodec/audioconvert.h"
|
||||
// #include "libavcodec/opt.h"
|
||||
|
||||
// #include "cmdutils.h"
|
||||
#ifdef HAS_RESAMPLE
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavresample/avresample.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
@@ -41,8 +41,8 @@
|
||||
|
||||
#undef exit
|
||||
|
||||
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
|
||||
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
|
||||
#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
|
||||
#define MIN_FRAMES 5
|
||||
|
||||
/* SDL audio buffer size, in samples. Should be small to have precise
|
||||
A/V sync as SDL does not have hardware buffer fullness info. */
|
||||
@@ -100,17 +100,24 @@ typedef struct VideoState {
|
||||
/* samples output by the codec. we reserve more space for avsync
|
||||
compensation */
|
||||
|
||||
#ifndef HAS_RESAMPLE
|
||||
uint8_t audio_buf1[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
|
||||
uint8_t audio_buf2[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
|
||||
|
||||
#else
|
||||
uint8_t *audio_buf1;
|
||||
#endif
|
||||
uint8_t *audio_buf;
|
||||
|
||||
unsigned int audio_buf_size; /* in bytes */
|
||||
|
||||
int audio_buf_index; /* in bytes */
|
||||
AVPacket audio_pkt;
|
||||
AVPacket audio_pkt_temp;
|
||||
|
||||
// AVAudioConvert *reformat_ctx;
|
||||
#ifndef HAS_RESAMPLE
|
||||
ReSampleContext *reformat_ctx;
|
||||
#endif
|
||||
int resample_frac;
|
||||
|
||||
int show_audio; /* if true, display audio samples */
|
||||
@@ -165,6 +172,19 @@ typedef struct VideoState {
|
||||
// Should we force the display of the current video frame?
|
||||
int first_frame;
|
||||
|
||||
#ifdef HAS_RESAMPLE
|
||||
// The audio frame, and the audio resample context.
|
||||
enum AVSampleFormat sdl_sample_fmt;
|
||||
uint64_t sdl_channel_layout;
|
||||
int sdl_channels;
|
||||
int sdl_sample_rate;
|
||||
enum AVSampleFormat resample_sample_fmt;
|
||||
uint64_t resample_channel_layout;
|
||||
int resample_sample_rate;
|
||||
AVAudioResampleContext *avr;
|
||||
AVFrame *frame;
|
||||
#endif
|
||||
|
||||
} VideoState;
|
||||
|
||||
SDL_mutex *codec_mutex = NULL;
|
||||
@@ -183,7 +203,6 @@ static int debug_mv = 0;
|
||||
static int workaround_bugs = 1;
|
||||
static int fast = 0;
|
||||
static int genpts = 0;
|
||||
static int lowres = 0;
|
||||
static int idct = FF_IDCT_AUTO;
|
||||
static enum AVDiscard skip_frame= AVDISCARD_DEFAULT;
|
||||
static enum AVDiscard skip_idct= AVDISCARD_DEFAULT;
|
||||
@@ -635,7 +654,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
|
||||
SDL_LockMutex(is->pictq_mutex);
|
||||
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
|
||||
!is->videoq.abort_request) {
|
||||
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
|
||||
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
|
||||
}
|
||||
SDL_UnlockMutex(is->pictq_mutex);
|
||||
|
||||
@@ -660,7 +679,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
|
||||
|
||||
/* wait until the picture is allocated */
|
||||
while (!vp->allocated && !is->videoq.abort_request) {
|
||||
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
|
||||
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
|
||||
}
|
||||
SDL_UnlockMutex(is->pictq_mutex);
|
||||
|
||||
@@ -770,8 +789,181 @@ static int video_thread(void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAS_RESAMPLE
|
||||
|
||||
/* decode one audio frame and returns its uncompressed size */
|
||||
static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
{
|
||||
AVPacket *pkt_temp = &is->audio_pkt_temp;
|
||||
AVPacket *pkt = &is->audio_pkt;
|
||||
AVCodecContext *dec = is->audio_st->codec;
|
||||
int n, len1, data_size, got_frame;
|
||||
double pts;
|
||||
int new_packet = 0;
|
||||
int flush_complete = 0;
|
||||
|
||||
for (;;) {
|
||||
/* NOTE: the audio packet can contain several frames */
|
||||
while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) {
|
||||
int resample_changed, audio_resample;
|
||||
|
||||
if (!is->frame) {
|
||||
if (!(is->frame = avcodec_alloc_frame()))
|
||||
return AVERROR(ENOMEM);
|
||||
} else
|
||||
avcodec_get_frame_defaults(is->frame);
|
||||
|
||||
if (flush_complete)
|
||||
break;
|
||||
new_packet = 0;
|
||||
len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp);
|
||||
if (len1 < 0) {
|
||||
/* if error, we skip the frame */
|
||||
pkt_temp->size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pkt_temp->data += len1;
|
||||
pkt_temp->size -= len1;
|
||||
|
||||
if (!got_frame) {
|
||||
/* stop sending empty packets if the decoder is finished */
|
||||
if (!pkt_temp->data && dec->codec->capabilities & CODEC_CAP_DELAY)
|
||||
flush_complete = 1;
|
||||
continue;
|
||||
}
|
||||
data_size = av_samples_get_buffer_size(NULL, dec->channels,
|
||||
is->frame->nb_samples,
|
||||
is->frame->format, 1);
|
||||
|
||||
audio_resample = is->frame->format != is->sdl_sample_fmt ||
|
||||
is->frame->channel_layout != is->sdl_channel_layout ||
|
||||
is->frame->sample_rate != is->sdl_sample_rate;
|
||||
|
||||
resample_changed = is->frame->format != is->resample_sample_fmt ||
|
||||
is->frame->channel_layout != is->resample_channel_layout ||
|
||||
is->frame->sample_rate != is->resample_sample_rate;
|
||||
|
||||
if ((!is->avr && audio_resample) || resample_changed) {
|
||||
int ret;
|
||||
if (is->avr)
|
||||
avresample_close(is->avr);
|
||||
else if (audio_resample) {
|
||||
is->avr = avresample_alloc_context();
|
||||
if (!is->avr) {
|
||||
fprintf(stderr, "error allocating AVAudioResampleContext\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (audio_resample) {
|
||||
av_opt_set_int(is->avr, "in_channel_layout", is->frame->channel_layout, 0);
|
||||
av_opt_set_int(is->avr, "in_sample_fmt", is->frame->format, 0);
|
||||
av_opt_set_int(is->avr, "in_sample_rate", is->frame->sample_rate, 0);
|
||||
av_opt_set_int(is->avr, "out_channel_layout", is->sdl_channel_layout, 0);
|
||||
av_opt_set_int(is->avr, "out_sample_fmt", is->sdl_sample_fmt, 0);
|
||||
av_opt_set_int(is->avr, "out_sample_rate", is->sdl_sample_rate, 0);
|
||||
|
||||
if ((ret = avresample_open(is->avr)) < 0) {
|
||||
fprintf(stderr, "error initializing libavresample\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
is->resample_sample_fmt = is->frame->format;
|
||||
is->resample_channel_layout = is->frame->channel_layout;
|
||||
is->resample_sample_rate = is->frame->sample_rate;
|
||||
}
|
||||
|
||||
if (audio_resample) {
|
||||
void *tmp_out;
|
||||
int out_samples, out_size, out_linesize;
|
||||
int osize = av_get_bytes_per_sample(is->sdl_sample_fmt);
|
||||
int nb_samples = is->frame->nb_samples;
|
||||
|
||||
int max_samples = 2 * (avresample_get_delay(is->avr) + nb_samples) * is->sdl_sample_rate / is->frame->sample_rate;
|
||||
|
||||
|
||||
out_size = av_samples_get_buffer_size(
|
||||
&out_linesize,
|
||||
is->sdl_channels,
|
||||
max_samples,
|
||||
is->sdl_sample_fmt, 0);
|
||||
|
||||
tmp_out = av_realloc(is->audio_buf1, out_size);
|
||||
|
||||
if (!tmp_out)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
is->audio_buf1 = tmp_out;
|
||||
|
||||
out_samples = avresample_convert(is->avr,
|
||||
&is->audio_buf1,
|
||||
out_linesize, max_samples,
|
||||
is->frame->data,
|
||||
is->frame->linesize[0],
|
||||
is->frame->nb_samples);
|
||||
|
||||
if (out_samples < 0) {
|
||||
fprintf(stderr, "avresample_convert() failed\n");
|
||||
break;
|
||||
}
|
||||
is->audio_buf = is->audio_buf1;
|
||||
data_size = out_samples * osize * is->sdl_channels;
|
||||
} else {
|
||||
is->audio_buf = is->frame->data[0];
|
||||
}
|
||||
|
||||
/* if no pts, then compute it */
|
||||
pts = is->audio_clock;
|
||||
*pts_ptr = pts;
|
||||
n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt);
|
||||
is->audio_clock += (double)data_size /
|
||||
(double)(n * is->sdl_sample_rate);
|
||||
|
||||
// This is Ren'Py specific code, to deal with ogg files with
|
||||
// more data than their duration.
|
||||
if (is->audio_duration) {
|
||||
int len = data_size / 4;
|
||||
int maxlen = is->audio_duration - is->audio_played;
|
||||
|
||||
if (len > maxlen) {
|
||||
len = maxlen;
|
||||
}
|
||||
|
||||
is->audio_played += len;
|
||||
data_size = len * 4;
|
||||
}
|
||||
|
||||
return data_size;
|
||||
}
|
||||
|
||||
/* free the current packet */
|
||||
if (pkt->data)
|
||||
av_free_packet(pkt);
|
||||
memset(pkt_temp, 0, sizeof(*pkt_temp));
|
||||
|
||||
if (is->paused || is->audioq.abort_request) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read next packet */
|
||||
if ((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
|
||||
return -1;
|
||||
|
||||
if (pkt->data == flush_pkt.data) {
|
||||
avcodec_flush_buffers(dec);
|
||||
flush_complete = 0;
|
||||
}
|
||||
|
||||
*pkt_temp = *pkt;
|
||||
|
||||
/* if update the audio clock with the pts */
|
||||
if (pkt->pts != AV_NOPTS_VALUE) {
|
||||
is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* decode one audio frame and returns its uncompressed size */
|
||||
static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
@@ -823,17 +1015,17 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
}
|
||||
|
||||
// Moved by tom, from below next block. Is this right?
|
||||
|
||||
|
||||
|
||||
if (is->reformat_ctx) {
|
||||
|
||||
|
||||
int len = data_size / av_get_bytes_per_sample(dec->sample_fmt);
|
||||
len /= dec->channels;
|
||||
|
||||
len = audio_resample(is->reformat_ctx, (short *) is->audio_buf2, (short *) is->audio_buf1, len);
|
||||
|
||||
data_size = len * 4;
|
||||
is->audio_buf = is->audio_buf2;
|
||||
is->audio_buf = is->audio_buf2;
|
||||
} else {
|
||||
is->audio_buf = is->audio_buf1;
|
||||
}
|
||||
@@ -846,7 +1038,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
int inpos;
|
||||
|
||||
int len = data_size / 4;
|
||||
|
||||
|
||||
if (is->audio_buf == is->audio_buf1) {
|
||||
in = (short *) is->audio_buf1;
|
||||
out = (short *) is->audio_buf2;
|
||||
@@ -860,9 +1052,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
// which seems reasonable.
|
||||
in[2 * len] = 2 * in[2 * len - 2] - in[2 * len - 4];
|
||||
in[2 * len + 1] = 2 * in[2 * len - 1] - in[2 * len - 3];
|
||||
|
||||
|
||||
// The number of bytes of input consumed per output
|
||||
// sample. Scaled by 1 << 14.
|
||||
// sample. Scaled by 1 << 14.
|
||||
in_per_out = (dec->sample_rate << 14) / audio_sample_rate;
|
||||
|
||||
len *= (1 << 14);
|
||||
@@ -892,14 +1084,14 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
out[2 * outpos + 1] = a + (((b - a) * frac) >> 14);
|
||||
|
||||
outpos++;
|
||||
inpos += in_per_out;
|
||||
inpos += in_per_out;
|
||||
}
|
||||
|
||||
// Store the fraction.
|
||||
is->resample_frac = inpos & ((1 << 14) - 1);
|
||||
|
||||
data_size = outpos * 4;
|
||||
is->audio_buf = (uint8_t *) out;
|
||||
is->audio_buf = (uint8_t *) out;
|
||||
}
|
||||
|
||||
|
||||
@@ -915,7 +1107,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
if (is->audio_duration) {
|
||||
int len = data_size / 4;
|
||||
int maxlen = is->audio_duration - is->audio_played;
|
||||
|
||||
|
||||
if (len > maxlen) {
|
||||
len = maxlen;
|
||||
}
|
||||
@@ -923,7 +1115,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
is->audio_played += len;
|
||||
data_size = len * 4;
|
||||
}
|
||||
|
||||
|
||||
return data_size;
|
||||
}
|
||||
|
||||
@@ -953,6 +1145,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* get the current audio output buffer size, in samples. With SDL, we
|
||||
cannot have a precise information */
|
||||
static int audio_write_get_buf_size(VideoState *is)
|
||||
@@ -1038,8 +1233,6 @@ static int stream_component_open(VideoState *is, int stream_index)
|
||||
enc->debug_mv = debug_mv;
|
||||
enc->debug = debug;
|
||||
enc->workaround_bugs = workaround_bugs;
|
||||
enc->lowres = lowres;
|
||||
if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;
|
||||
enc->idct_algo= idct;
|
||||
if(fast) enc->flags2 |= CODEC_FLAG2_FAST;
|
||||
enc->skip_frame= skip_frame;
|
||||
@@ -1069,6 +1262,20 @@ static int stream_component_open(VideoState *is, int stream_index)
|
||||
is->audio_buf_size = 0;
|
||||
is->audio_buf_index = 0;
|
||||
|
||||
#ifdef HAS_RESAMPLE
|
||||
if (!enc->channel_layout)
|
||||
enc->channel_layout = av_get_default_channel_layout(enc->channels);
|
||||
if (!enc->channel_layout) {
|
||||
fprintf(stderr, "%s: unable to guess channel layout\n", is->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
is->sdl_sample_rate = audio_sample_rate;
|
||||
is->sdl_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout);
|
||||
is->sdl_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
#endif
|
||||
|
||||
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
|
||||
packet_queue_init(&is->audioq);
|
||||
break;
|
||||
@@ -1102,8 +1309,10 @@ static void stream_component_close(VideoState *is, int stream_index)
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
packet_queue_abort(&is->audioq);
|
||||
packet_queue_end(&is->audioq);
|
||||
#ifndef HAS_RESAMPLE
|
||||
if (is->reformat_ctx)
|
||||
audio_resample_close(is->reformat_ctx);
|
||||
#endif
|
||||
break;
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
packet_queue_abort(&is->videoq);
|
||||
@@ -1290,12 +1499,12 @@ static int decode_thread(void *arg)
|
||||
if (is->abort_request) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* if the queue are full, no need to read more */
|
||||
if (is->audioq.size > MAX_AUDIOQ_SIZE ||
|
||||
is->videoq.size > MAX_VIDEOQ_SIZE) {
|
||||
|
||||
/* wait 2 ms - or wait for quit notify.*/
|
||||
if ((is->audioq.size > MIN_AUDIOQ_SIZE || is->audio_stream < 0) &&
|
||||
(is->videoq.nb_packets > MIN_FRAMES || is->video_stream < 0)) {
|
||||
|
||||
/* wait 2 ms - or wait for quit notify.*/
|
||||
SDL_LockMutex(is->quit_mutex);
|
||||
SDL_CondWaitTimeout(is->quit_cond, is->quit_mutex, 2);
|
||||
SDL_UnlockMutex(is->quit_mutex);
|
||||
@@ -1353,7 +1562,7 @@ fail:
|
||||
if (is->video_stream >= 0)
|
||||
stream_component_close(is, is->video_stream);
|
||||
if (is->ic) {
|
||||
av_close_input_file(is->ic);
|
||||
avformat_close_input(&(is->ic));
|
||||
is->ic = NULL;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
#if defined ANDROID
|
||||
|
||||
#define RENPY_GLES_1
|
||||
#define RENPY_GLES_2
|
||||
|
||||
#elif defined ANGLE
|
||||
|
||||
@@ -54,7 +54,10 @@
|
||||
|
||||
#if defined RENPY_GLES_2
|
||||
|
||||
#ifndef ANDROID
|
||||
#include <EGL/egl.h>
|
||||
#endif
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
|
||||
typedef GLuint GLhandleARB;
|
||||
|
||||
+19
-16
@@ -373,25 +373,28 @@ cdef extern from "pyfreetype.h":
|
||||
FT_Error FT_Set_Char_Size(FT_Face, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt hres, FT_UInt vres)
|
||||
FT_Error FT_Set_Pixel_Sizes(FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height)
|
||||
|
||||
ctypedef enum FT_Load_Flags:
|
||||
FT_LOAD_DEFAULT
|
||||
FT_LOAD_NO_SCALE
|
||||
FT_LOAD_NO_HINTING
|
||||
FT_LOAD_RENDER
|
||||
FT_LOAD_NO_BITMAP
|
||||
FT_LOAD_VERTICAL_LAYOUT
|
||||
FT_LOAD_FORCE_AUTOHINT
|
||||
FT_LOAD_CROP_BITMAP
|
||||
FT_LOAD_PEDANTIC
|
||||
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
|
||||
FT_LOAD_NO_RECURSE
|
||||
FT_LOAD_IGNORE_TRANSFORM
|
||||
FT_LOAD_MONOCHROME
|
||||
FT_LOAD_LINEAR_DESIGN
|
||||
FT_LOAD_SBITS_ONLY
|
||||
FT_LOAD_NO_AUTOHINT
|
||||
|
||||
FT_Error FT_Load_Glyph(FT_Face face, FT_UInt glyph_index, FT_Int32 flags)
|
||||
FT_Error FT_Load_Char(FT_Face face, FT_ULong char_code, FT_Int32 flags)
|
||||
|
||||
DEF FT_LOAD_DEFAULT = 0
|
||||
DEF FT_LOAD_NO_SCALE = 1 << 0
|
||||
DEF FT_LOAD_NO_HINTING = 1 << 1
|
||||
DEF FT_LOAD_RENDER = 1 << 2
|
||||
DEF FT_LOAD_NO_BITMAP = 1 << 3
|
||||
DEF FT_LOAD_VERTICAL_LAYOUT = 1 << 4
|
||||
DEF FT_LOAD_FORCE_AUTOHINT = 1 << 5
|
||||
DEF FT_LOAD_CROP_BITMAP = 1 << 6
|
||||
DEF FT_LOAD_PEDANTIC = 1 << 7
|
||||
DEF FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9
|
||||
DEF FT_LOAD_NO_RECURSE = 1 << 10
|
||||
DEF FT_LOAD_IGNORE_TRANSFORM = 1 << 11
|
||||
DEF FT_LOAD_MONOCHROME = 1 << 12
|
||||
DEF FT_LOAD_LINEAR_DESIGN = 1 << 13
|
||||
DEF FT_LOAD_SBITS_ONLY = 1 << 14
|
||||
DEF FT_LOAD_NO_AUTOHINT = 1 << 15
|
||||
|
||||
|
||||
void FT_Set_Transform(FT_Face face, FT_Matrix *matrix, FT_Vector *delta)
|
||||
|
||||
|
||||
@@ -3,6 +3,21 @@ cdef extern from "pygame/pygame.h":
|
||||
int w
|
||||
int h
|
||||
int pitch
|
||||
int flags
|
||||
void *pixels
|
||||
|
||||
struct SDL_Rect:
|
||||
int x
|
||||
int y
|
||||
int w
|
||||
int h
|
||||
|
||||
SDL_Surface *PySurface_AsSurface(object)
|
||||
int SDL_SetAlpha(SDL_Surface *surface, unsigned int flag, char alpha)
|
||||
|
||||
enum:
|
||||
SDL_SRCALPHA
|
||||
|
||||
cdef extern int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) nogil
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include <Python.h>
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj);
|
||||
|
||||
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight, int paused);
|
||||
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight);
|
||||
void PSS_stop(int channel);
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
cdef extern from "pss.h":
|
||||
|
||||
cdef extern from "pygame/pygame.h":
|
||||
cdef struct SDL_RWops:
|
||||
pass
|
||||
|
||||
void import_pygame_rwobject()
|
||||
SDL_RWops* RWopsFromPythonThreaded(object obj)
|
||||
|
||||
cdef extern from "pss.h":
|
||||
|
||||
void PSS_play(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight, int paused)
|
||||
void PSS_queue(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight)
|
||||
void PSS_stop(int channel)
|
||||
@@ -181,3 +184,5 @@ def movie_size():
|
||||
def check_version(version):
|
||||
if version < 2 or version > 4:
|
||||
raise Exception("pysdlsound version mismatch.")
|
||||
|
||||
import_pygame_rwobject()
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <Python.h>
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
SDL_RWops* RWopsFromPython(PyObject* obj);
|
||||
|
||||
void core_init(void);
|
||||
|
||||
void save_png_core(PyObject *pysurf, SDL_RWops *file, int compress);
|
||||
|
||||
@@ -1,859 +0,0 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* font module for pygame
|
||||
*/
|
||||
#include <Python.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pygame/pygame.h>
|
||||
#include "structmember.h"
|
||||
#include "renpy_ttf.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
RENPY_TTF_Font* font;
|
||||
PyObject* weakreflist;
|
||||
} PyFontObject;
|
||||
#define PyFont_AsFont(x) (((PyFontObject*)x)->font)
|
||||
|
||||
static int font_initialized = 0;
|
||||
static char* font_defaultname = "freesansbold.ttf";
|
||||
static PyObject* self_module = NULL;
|
||||
|
||||
static char* pkgdatamodule_name = "pygame.pkgdata";
|
||||
static char* resourcefunc_name = "getResource";
|
||||
|
||||
static PyObject *font_resource(char *filename) {
|
||||
PyObject* load_basicfunc = NULL;
|
||||
PyObject* pkgdatamodule = NULL;
|
||||
PyObject* resourcefunc = NULL;
|
||||
PyObject* result = NULL;
|
||||
|
||||
pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name);
|
||||
if (!pkgdatamodule) goto font_resource_end;
|
||||
|
||||
resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name);
|
||||
if (!resourcefunc) goto font_resource_end;
|
||||
|
||||
result = PyObject_CallFunction(resourcefunc, "s", filename);
|
||||
if (!result) goto font_resource_end;
|
||||
|
||||
if (PyFile_Check(result)) {
|
||||
PyObject *tmp = PyFile_Name(result);
|
||||
Py_INCREF(tmp);
|
||||
Py_DECREF(result);
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
font_resource_end:
|
||||
Py_XDECREF(pkgdatamodule);
|
||||
Py_XDECREF(resourcefunc);
|
||||
Py_XDECREF(load_basicfunc);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void font_autoquit(void)
|
||||
{
|
||||
if(font_initialized)
|
||||
{
|
||||
font_initialized = 0;
|
||||
RENPY_TTF_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PyObject* font_autoinit(PyObject* self, PyObject* arg)
|
||||
{
|
||||
if(!PyArg_ParseTuple(arg, ""))
|
||||
return NULL;
|
||||
|
||||
if(!font_initialized)
|
||||
{
|
||||
PyGame_RegisterQuit(font_autoquit);
|
||||
|
||||
if(RENPY_TTF_Init())
|
||||
return PyInt_FromLong(0);
|
||||
font_initialized = 1;
|
||||
|
||||
}
|
||||
return PyInt_FromLong(font_initialized);
|
||||
}
|
||||
|
||||
|
||||
/*DOC*/ static char doc_quit[] =
|
||||
/*DOC*/ "pygame.font.quit() -> none\n"
|
||||
/*DOC*/ "uninitialize the font module\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Manually uninitialize SDL_ttf's font system. It is safe to call\n"
|
||||
/*DOC*/ "this if font is currently not initialized.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* fontmodule_quit(PyObject* self, PyObject* arg)
|
||||
{
|
||||
if(!PyArg_ParseTuple(arg, ""))
|
||||
return NULL;
|
||||
|
||||
font_autoquit();
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*DOC*/ static char doc_init[] =
|
||||
/*DOC*/ "pygame.font.init() -> None\n"
|
||||
/*DOC*/ "initialize the display module\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Manually initialize the font module. Will raise an exception if\n"
|
||||
/*DOC*/ "it cannot be initialized. It is safe to call this function if\n"
|
||||
/*DOC*/ "font is currently initialized.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* fontmodule_init(PyObject* self, PyObject* arg)
|
||||
{
|
||||
PyObject* result;
|
||||
int istrue;
|
||||
|
||||
if(!PyArg_ParseTuple(arg, ""))
|
||||
return NULL;
|
||||
|
||||
result = font_autoinit(self, arg);
|
||||
istrue = PyObject_IsTrue(result);
|
||||
Py_DECREF(result);
|
||||
if(!istrue)
|
||||
return RAISE(PyExc_SDLError, SDL_GetError());
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_get_init[] =
|
||||
/*DOC*/ "pygame.font.get_init() -> bool\n"
|
||||
/*DOC*/ "get status of font module initialization\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Returns true if the font module is currently intialized.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* get_init(PyObject* self, PyObject* arg)
|
||||
{
|
||||
if(!PyArg_ParseTuple(arg, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(font_initialized);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* font object methods */
|
||||
|
||||
/*DOC*/ static char doc_font_get_height[] =
|
||||
/*DOC*/ "Font.get_height() -> int\n"
|
||||
/*DOC*/ "average height of font glyph\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Returns the average size of each glyph in the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_height(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(RENPY_TTF_FontHeight(font));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_descent[] =
|
||||
/*DOC*/ "Font.get_descent() -> int\n"
|
||||
/*DOC*/ "gets the font descent\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Returns the descent for the font. The descent is the number of\n"
|
||||
/*DOC*/ "pixels from the font baseline to the bottom of the font.\n"
|
||||
/*DOC*/ "With most fonts this is a negative number.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_descent(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(RENPY_TTF_FontDescent(font));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_ascent[] =
|
||||
/*DOC*/ "Font.get_ascent() -> int\n"
|
||||
/*DOC*/ "gets the font ascent\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Returns the ascent for the font. The ascent is the number of\n"
|
||||
/*DOC*/ "pixels from the font baseline to the top of the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_ascent(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(RENPY_TTF_FontAscent(font));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_linesize[] =
|
||||
/*DOC*/ "Font.get_linesize() -> int\n"
|
||||
/*DOC*/ "gets the font recommended linesize\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Returns the linesize for the font. Each font comes with it's own\n"
|
||||
/*DOC*/ "recommendation for the spacing number of pixels between each line\n"
|
||||
/*DOC*/ "of the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_linesize(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(RENPY_TTF_FontLineSkip(font));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_bold[] =
|
||||
/*DOC*/ "Font.get_bold() -> bool\n"
|
||||
/*DOC*/ "status of the bold attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Get the current status of the font's bold attribute\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_bold(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_BOLD) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_set_bold[] =
|
||||
/*DOC*/ "Font.set_bold(bool) -> None\n"
|
||||
/*DOC*/ "assign the bold attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Enables or disables the bold attribute for the font. Making the\n"
|
||||
/*DOC*/ "font bold does not work as well as you expect.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_set_bold(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
int style, val;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "i", &val))
|
||||
return NULL;
|
||||
|
||||
style = RENPY_TTF_GetFontStyle(font);
|
||||
if(val)
|
||||
style |= RENPY_TTF_STYLE_BOLD;
|
||||
else
|
||||
style &= ~RENPY_TTF_STYLE_BOLD;
|
||||
RENPY_TTF_SetFontStyle(font, style);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_italic[] =
|
||||
/*DOC*/ "Font.get_italic() -> bool\n"
|
||||
/*DOC*/ "status of the italic attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Get the current status of the font's italic attribute\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_italic(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_ITALIC) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_set_italic[] =
|
||||
/*DOC*/ "Font.set_italic(bool) -> None\n"
|
||||
/*DOC*/ "assign the italic attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Enables or disables the italic attribute for the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_set_italic(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
int style, val;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "i", &val))
|
||||
return NULL;
|
||||
|
||||
style = RENPY_TTF_GetFontStyle(font);
|
||||
if(val)
|
||||
style |= RENPY_TTF_STYLE_ITALIC;
|
||||
else
|
||||
style &= ~RENPY_TTF_STYLE_ITALIC;
|
||||
RENPY_TTF_SetFontStyle(font, style);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_get_underline[] =
|
||||
/*DOC*/ "Font.get_underline() -> bool\n"
|
||||
/*DOC*/ "status of the underline attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Get the current status of the font's underline attribute\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_get_underline(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_UNDERLINE) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_set_underline[] =
|
||||
/*DOC*/ "Font.set_underline(bool) -> None\n"
|
||||
/*DOC*/ "assign the underline attribute\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Enables or disables the underline attribute for the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_set_underline(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
int style, val;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "i", &val))
|
||||
return NULL;
|
||||
|
||||
style = RENPY_TTF_GetFontStyle(font);
|
||||
if(val)
|
||||
style |= RENPY_TTF_STYLE_UNDERLINE;
|
||||
else
|
||||
style &= ~RENPY_TTF_STYLE_UNDERLINE;
|
||||
RENPY_TTF_SetFontStyle(font, style);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_render[] =
|
||||
/*DOC*/ "Font.render(text, antialias, fore_RGBA, [back_RGBA]) -> Surface\n"
|
||||
/*DOC*/ "render text to a new image\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Render the given text onto a new image surface. The given text\n"
|
||||
/*DOC*/ "can be standard python text or unicode. Antialiasing will smooth\n"
|
||||
/*DOC*/ "the edges of the font for a much cleaner look. The foreground\n"
|
||||
/*DOC*/ "and background color are both RGBA, the alpha component is ignored\n"
|
||||
/*DOC*/ "if given. If the background color is omitted, the text will have a\n"
|
||||
/*DOC*/ "transparent background.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Note that font rendering is not thread safe, therefore only one\n"
|
||||
/*DOC*/ "thread can render text at any given time.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Also, rendering smooth text with underlines will crash with SDL_ttf\n"
|
||||
/*DOC*/ "less that version 2.0, be careful.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "If you pass an empty string, render() will return a blank surface\n"
|
||||
/*DOC*/ "1 pixel wide and the same height as the font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_render(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
int aa;
|
||||
PyObject* text, *final;
|
||||
PyObject* fg_rgba_obj, *bg_rgba_obj = NULL;
|
||||
Uint8 rgba[4];
|
||||
SDL_Surface* surf;
|
||||
SDL_Color foreg, backg;
|
||||
if(!PyArg_ParseTuple(args, "OiO|O", &text, &aa, &fg_rgba_obj, &bg_rgba_obj))
|
||||
return NULL;
|
||||
|
||||
if(!RGBAFromObj(fg_rgba_obj, rgba))
|
||||
return RAISE(PyExc_TypeError, "Invalid foreground RGBA argument");
|
||||
foreg.r = rgba[0]; foreg.g = rgba[1]; foreg.b = rgba[2];
|
||||
if(bg_rgba_obj)
|
||||
{
|
||||
if(!RGBAFromObj(bg_rgba_obj, rgba))
|
||||
return RAISE(PyExc_TypeError, "Invalid background RGBA argument");
|
||||
backg.r = rgba[0];
|
||||
backg.g = rgba[1];
|
||||
backg.b = rgba[2];
|
||||
backg.unused = 0;
|
||||
} else {
|
||||
backg.r = 0;
|
||||
backg.g = 0;
|
||||
backg.b = 0;
|
||||
backg.unused = 0;
|
||||
}
|
||||
|
||||
|
||||
if(!PyObject_IsTrue(text))
|
||||
{
|
||||
int height = RENPY_TTF_FontHeight(font);
|
||||
surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, height, 32, 0xff<<16, 0xff<<8, 0xff, 0);
|
||||
if(bg_rgba_obj)
|
||||
{
|
||||
Uint32 c = SDL_MapRGB(surf->format, backg.r, backg.g, backg.b);
|
||||
SDL_FillRect(surf, NULL, c);
|
||||
}
|
||||
else
|
||||
SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
|
||||
}
|
||||
else if(PyUnicode_Check(text))
|
||||
{
|
||||
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
|
||||
char *string = PyString_AsString(strob);
|
||||
|
||||
if(aa)
|
||||
{
|
||||
if(!bg_rgba_obj)
|
||||
surf = RENPY_TTF_RenderUTF8_Blended(font, string, foreg);
|
||||
else
|
||||
surf = RENPY_TTF_RenderUTF8_Shaded(font, string, foreg, backg);
|
||||
}
|
||||
else
|
||||
surf = RENPY_TTF_RenderUTF8_Solid(font, string, foreg);
|
||||
|
||||
Py_DECREF(strob);
|
||||
}
|
||||
else if(PyString_Check(text))
|
||||
{
|
||||
char* string = PyString_AsString(text);
|
||||
if(aa)
|
||||
{
|
||||
if(!bg_rgba_obj)
|
||||
surf = RENPY_TTF_RenderText_Blended(font, string, foreg);
|
||||
else
|
||||
surf = RENPY_TTF_RenderText_Shaded(font, string, foreg, backg);
|
||||
}
|
||||
else
|
||||
surf = RENPY_TTF_RenderText_Solid(font, string, foreg);
|
||||
}
|
||||
else
|
||||
return RAISE(PyExc_TypeError, "text must be a string or unicode");
|
||||
|
||||
if(!surf)
|
||||
return RAISE(PyExc_SDLError, "SDL_ttf render failed");
|
||||
|
||||
if(!aa && bg_rgba_obj) /*turn off transparancy*/
|
||||
{
|
||||
SDL_SetColorKey(surf, 0, 0);
|
||||
surf->format->palette->colors[0].r = backg.r;
|
||||
surf->format->palette->colors[0].g = backg.g;
|
||||
surf->format->palette->colors[0].b = backg.b;
|
||||
}
|
||||
|
||||
final = PySurface_New(surf);
|
||||
if(!final)
|
||||
SDL_FreeSurface(surf);
|
||||
return final;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_font_size[] =
|
||||
/*DOC*/ "Font.size(text) -> width, height\n"
|
||||
/*DOC*/ "size of rendered text\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Computes the rendered size of the given text. The text can be\n"
|
||||
/*DOC*/ "standard python text or unicode. Changing the bold and italic\n"
|
||||
/*DOC*/ "attributes can change the size of the rendered text.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* font_size(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
int w, h;
|
||||
PyObject* text;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "O", &text))
|
||||
return NULL;
|
||||
|
||||
if(PyUnicode_Check(text))
|
||||
{
|
||||
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
|
||||
char *string = PyString_AsString(strob);
|
||||
|
||||
RENPY_TTF_SizeUTF8(font, string, &w, &h);
|
||||
|
||||
Py_DECREF(strob);
|
||||
}
|
||||
else if(PyString_Check(text))
|
||||
{
|
||||
char* string = PyString_AsString(text);
|
||||
RENPY_TTF_SizeText(font, string, &w, &h);
|
||||
}
|
||||
else
|
||||
return RAISE(PyExc_TypeError, "text must be a string or unicode");
|
||||
|
||||
return Py_BuildValue("(ii)", w, h);
|
||||
}
|
||||
|
||||
static PyObject* font_set_expand(PyObject* self, PyObject* args)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
float expand;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "f", &expand))
|
||||
return NULL;
|
||||
|
||||
RENPY_TTF_SetExpand(font, expand);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyMethodDef font_methods[] =
|
||||
{
|
||||
{ "get_height", font_get_height, 1, doc_font_get_height },
|
||||
{ "get_descent", font_get_descent, 1, doc_font_get_descent },
|
||||
{ "get_ascent", font_get_ascent, 1, doc_font_get_ascent },
|
||||
{ "get_linesize", font_get_linesize, 1, doc_font_get_linesize },
|
||||
|
||||
{ "get_bold", font_get_bold, 1, doc_font_get_bold },
|
||||
{ "set_bold", font_set_bold, 1, doc_font_set_bold },
|
||||
{ "get_italic", font_get_italic, 1, doc_font_get_italic },
|
||||
{ "set_italic", font_set_italic, 1, doc_font_set_italic },
|
||||
{ "get_underline", font_get_underline, 1, doc_font_get_underline },
|
||||
{ "set_underline", font_set_underline, 1, doc_font_set_underline },
|
||||
|
||||
{ "render", font_render, 1, doc_font_render },
|
||||
{ "size", font_size, 1, doc_font_size },
|
||||
|
||||
{ "set_expand", font_set_expand, 1, ""},
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*font object internals*/
|
||||
|
||||
static void font_dealloc(PyFontObject* self)
|
||||
{
|
||||
RENPY_TTF_Font* font = PyFont_AsFont(self);
|
||||
|
||||
if(font && font_initialized)
|
||||
RENPY_TTF_CloseFont(font);
|
||||
|
||||
if(self->weakreflist)
|
||||
PyObject_ClearWeakRefs((PyObject*)self);
|
||||
self->ob_type->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
|
||||
static int font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
int index = 0;
|
||||
int fontsize;
|
||||
RENPY_TTF_Font* font = NULL;
|
||||
PyObject* fileobj;
|
||||
|
||||
self->font = NULL;
|
||||
if(!PyArg_ParseTuple(args, "Oi|i", &fileobj, &fontsize, &index))
|
||||
return -1;
|
||||
|
||||
if(!font_initialized)
|
||||
{
|
||||
RAISE(PyExc_SDLError, "font not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_INCREF(fileobj);
|
||||
|
||||
if(fontsize <= 1)
|
||||
fontsize = 1;
|
||||
|
||||
if(fileobj == Py_None) {
|
||||
Py_DECREF(fileobj);
|
||||
fileobj = font_resource(font_defaultname);
|
||||
if(!fileobj)
|
||||
{
|
||||
RAISE(PyExc_RuntimeError, "default font not found");
|
||||
return -1;
|
||||
}
|
||||
fontsize = (int)(fontsize * .6875);
|
||||
if(fontsize <= 1)
|
||||
fontsize = 1;
|
||||
}
|
||||
|
||||
if(PyString_Check(fileobj) || PyUnicode_Check(fileobj))
|
||||
{
|
||||
FILE* test;
|
||||
char* filename = PyString_AsString(fileobj);
|
||||
Py_DECREF(fileobj);
|
||||
fileobj = NULL;
|
||||
|
||||
if(!filename)
|
||||
return -1;
|
||||
|
||||
/*check if it is a valid file, else SDL_ttf segfaults*/
|
||||
test = fopen(filename, "rb");
|
||||
if(!test)
|
||||
{
|
||||
if (!strcmp(filename, font_defaultname))
|
||||
{
|
||||
fileobj = font_resource(font_defaultname);
|
||||
}
|
||||
if (!fileobj) {
|
||||
PyErr_SetString(PyExc_IOError, "unable to read font filename");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(test);
|
||||
// Py_BEGIN_ALLOW_THREADS
|
||||
font = RENPY_TTF_OpenFont(filename, fontsize);
|
||||
// Py_END_ALLOW_THREADS
|
||||
}
|
||||
}
|
||||
if (!font)
|
||||
{
|
||||
#ifdef RENPY_TTF_MAJOR_VERSION
|
||||
SDL_RWops *rw;
|
||||
rw = RWopsFromPython(fileobj);
|
||||
if (!rw) {
|
||||
Py_DECREF(fileobj);
|
||||
return -1;
|
||||
}
|
||||
// Py_BEGIN_ALLOW_THREADS
|
||||
font = RENPY_TTF_OpenFontIndexRW(rw, 1, fontsize, index);
|
||||
// Py_END_ALLOW_THREADS
|
||||
#else
|
||||
Py_DECREF(fileobj);
|
||||
RAISE(PyExc_NotImplementedError, "nonstring fonts require SDL_ttf-2.0.6");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!font)
|
||||
{
|
||||
RAISE(PyExc_RuntimeError, SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->font = font;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*DOC*/ static char doc_Font_MODULE[] =
|
||||
/*DOC*/ "The font object is created only from pygame.font.Font(). Once a\n"
|
||||
/*DOC*/ "font is created it's size and TTF file cannot be changed. The\n"
|
||||
/*DOC*/ "Font objects are mainly used to render() text into a new Surface.\n"
|
||||
/*DOC*/ "The Font objects also have a few states that can be set with\n"
|
||||
/*DOC*/ "set_underline(bool), set_bold(bool), set_italic(bool). Each of\n"
|
||||
/*DOC*/ "these functions contains an equivalent get_XXX() routine to find\n"
|
||||
/*DOC*/ "the current state. There are also many routines to query the\n"
|
||||
/*DOC*/ "dimensions of the text. The rendering functions work with both\n"
|
||||
/*DOC*/ "normal python strings, as well as with unicode strings.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyTypeObject PyFont_Type =
|
||||
{
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"pygame.font.Font",
|
||||
sizeof(PyFontObject),
|
||||
0,
|
||||
(destructor)font_dealloc,
|
||||
0,
|
||||
0, /*getattr*/
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
(hashfunc)NULL,
|
||||
(ternaryfunc)NULL,
|
||||
(reprfunc)NULL,
|
||||
0L,0L,0L,
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
doc_Font_MODULE, /* Documentation string */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
offsetof(PyFontObject, weakreflist), /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
font_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
(initproc)font_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
//PyType_GenericNew, /* tp_new */
|
||||
|
||||
|
||||
/*font module methods*/
|
||||
|
||||
/*DOC*/ static char doc_get_default_font[] =
|
||||
/*DOC*/ "pygame.font.get_default_font() -> string\n"
|
||||
/*DOC*/ "get the name of the default font\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "returns the filename for the default truetype font.\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
static PyObject* get_default_font(PyObject* self, PyObject* args)
|
||||
{
|
||||
if(!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
return PyString_FromString(font_defaultname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*font module methods*/
|
||||
#if 0
|
||||
/*DOC*/ static char doc_Font[] =
|
||||
/*DOC*/ "pygame.font.Font(file, size) -> Font\n"
|
||||
/*DOC*/ "create a new font object\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "This will create a new font object. The given file can\n"
|
||||
/*DOC*/ "be a filename or any python file-like object.\n"
|
||||
/*DOC*/ "The size represents the height of the font in\n"
|
||||
/*DOC*/ "pixels. The file argument can be 'None', which will\n"
|
||||
/*DOC*/ "use a plain default font.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "You must have at least SDL_ttf-2.0.6 for file object\n"
|
||||
/*DOC*/ "support. You can load TTF and FON fonts.\n"
|
||||
/*DOC*/ ;
|
||||
#endif
|
||||
|
||||
|
||||
static PyMethodDef font_builtins[] =
|
||||
{
|
||||
{ "__PYGAMEinit__", font_autoinit, 1, doc_init },
|
||||
{ "init", fontmodule_init, 1, doc_init },
|
||||
{ "quit", fontmodule_quit, 1, doc_quit },
|
||||
{ "get_init", get_init, 1, doc_get_init },
|
||||
{ "get_default_font", get_default_font, 1, doc_get_default_font },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
|
||||
static PyObject* PyFont_New(RENPY_TTF_Font* font)
|
||||
{
|
||||
PyFontObject* fontobj;
|
||||
|
||||
if(!font)
|
||||
return RAISE(PyExc_RuntimeError, "unable to load font.");
|
||||
fontobj = (PyFontObject *)PyFont_Type.tp_new(&PyFont_Type, NULL, NULL);
|
||||
|
||||
if(fontobj)
|
||||
fontobj->font = font;
|
||||
|
||||
return (PyObject*)fontobj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*DOC*/ static char doc_pygame_font_MODULE[] =
|
||||
/*DOC*/ "The font module allows for rendering TrueType fonts into a new\n"
|
||||
/*DOC*/ "Surface object. This module is optional and requires SDL_ttf as a\n"
|
||||
/*DOC*/ "dependency. You may want to check for pygame.font to import and\n"
|
||||
/*DOC*/ "initialize before attempting to use the module.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "Most of the work done with fonts are done by using the actual\n"
|
||||
/*DOC*/ "Font objects. The module by itself only has routines to\n"
|
||||
/*DOC*/ "initialize the module and create Font objects with\n"
|
||||
/*DOC*/ "pygame.font.Font().\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ "You can load fonts from the standard system fonts by using the\n"
|
||||
/*DOC*/ "pygame.font.SysFont() method. There are also other functions to\n"
|
||||
/*DOC*/ "help you work with system fonts.\n"
|
||||
/*DOC*/ "\n"
|
||||
/*DOC*/ ;
|
||||
|
||||
PYGAME_EXPORT
|
||||
void init_renpy_font(void)
|
||||
{
|
||||
PyObject *module;
|
||||
|
||||
if (PyType_Ready(&PyFont_Type) < 0)
|
||||
return;
|
||||
|
||||
/* create the module */
|
||||
PyFont_Type.ob_type = &PyType_Type;
|
||||
PyFont_Type.tp_new = &PyType_GenericNew;
|
||||
|
||||
module = Py_InitModule3("_renpy_font", font_builtins, doc_pygame_font_MODULE);
|
||||
self_module = module;
|
||||
|
||||
Py_INCREF((PyObject*)&PyFont_Type);
|
||||
PyModule_AddObject(module, "FontType", (PyObject *)&PyFont_Type);
|
||||
Py_INCREF((PyObject*)&PyFont_Type);
|
||||
PyModule_AddObject(module, "Font", (PyObject *)&PyFont_Type);
|
||||
|
||||
import_pygame_base();
|
||||
import_pygame_surface();
|
||||
import_pygame_rwobject();
|
||||
}
|
||||
|
||||
-1796
File diff suppressed because it is too large
Load Diff
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 1997-2004 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
/* $Id: SDL_ttf.h 2387 2006-05-11 09:03:37Z slouken $ */
|
||||
|
||||
/* This library is a wrapper around the excellent FreeType 2.0 library,
|
||||
available at:
|
||||
http://www.freetype.org/
|
||||
*/
|
||||
|
||||
#ifndef _RENPY_TTF_H
|
||||
#define _RENPY_TTF_H
|
||||
|
||||
#include "SDL.h"
|
||||
#include "begin_code.h"
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
|
||||
*/
|
||||
#define RENPY_RENPY_TTF_MAJOR_VERSION 2
|
||||
#define RENPY_RENPY_TTF_MINOR_VERSION 0
|
||||
#define RENPY_RENPY_TTF_PATCHLEVEL 8
|
||||
|
||||
/* This macro can be used to fill a version structure with the compile-time
|
||||
* version of the SDL_ttf library.
|
||||
*/
|
||||
#define RENPY_RENPY_TTF_VERSION(X) \
|
||||
{ \
|
||||
(X)->major = RENPY_RENPY_TTF_MAJOR_VERSION; \
|
||||
(X)->minor = RENPY_RENPY_TTF_MINOR_VERSION; \
|
||||
(X)->patch = RENPY_RENPY_TTF_PATCHLEVEL; \
|
||||
}
|
||||
|
||||
/* Backwards compatibility */
|
||||
#define RENPY_TTF_MAJOR_VERSION RENPY_RENPY_TTF_MAJOR_VERSION
|
||||
#define RENPY_TTF_MINOR_VERSION RENPY_RENPY_TTF_MINOR_VERSION
|
||||
#define RENPY_TTF_PATCHLEVEL RENPY_RENPY_TTF_PATCHLEVEL
|
||||
#define RENPY_TTF_VERSION(X) RENPY_RENPY_TTF_VERSION(X)
|
||||
|
||||
/* This function gets the version of the dynamically linked SDL_ttf library.
|
||||
it should NOT be used to fill a version structure, instead you should
|
||||
use the RENPY_RENPY_TTF_VERSION() macro.
|
||||
*/
|
||||
extern DECLSPEC const SDL_version * SDLCALL RENPY_TTF_Linked_Version(void);
|
||||
|
||||
/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
|
||||
#define UNICODE_BOM_NATIVE 0xFEFF
|
||||
#define UNICODE_BOM_SWAPPED 0xFFFE
|
||||
|
||||
/* This function tells the library whether UNICODE text is generally
|
||||
byteswapped. A UNICODE BOM character in a string will override
|
||||
this setting for the remainder of that string.
|
||||
*/
|
||||
extern DECLSPEC void SDLCALL RENPY_TTF_ByteSwappedUNICODE(int swapped);
|
||||
|
||||
/* The internal structure containing font information */
|
||||
typedef struct _RENPY_TTF_Font RENPY_TTF_Font;
|
||||
|
||||
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_Init(void);
|
||||
|
||||
/* Open a font file and create a font of the specified point size.
|
||||
* Some .fon fonts will have several sizes embedded in the file, so the
|
||||
* point size becomes the index of choosing which size. If the value
|
||||
* is too high, the last indexed size will be the default. */
|
||||
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFont(const char *file, int ptsize);
|
||||
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndex(const char *file, int ptsize, long index);
|
||||
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
|
||||
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
|
||||
|
||||
/* Set and retrieve the font style
|
||||
This font style is implemented by modifying the font glyphs, and
|
||||
doesn't reflect any inherent properties of the truetype font file.
|
||||
*/
|
||||
#define RENPY_TTF_STYLE_NORMAL 0x00
|
||||
#define RENPY_TTF_STYLE_BOLD 0x01
|
||||
#define RENPY_TTF_STYLE_ITALIC 0x02
|
||||
#define RENPY_TTF_STYLE_UNDERLINE 0x04
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_GetFontStyle(RENPY_TTF_Font *font);
|
||||
extern DECLSPEC void SDLCALL RENPY_TTF_SetFontStyle(RENPY_TTF_Font *font, int style);
|
||||
|
||||
/* Get the total height of the font - usually equal to point size */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_FontHeight(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the offset from the baseline to the top of the font
|
||||
This is a positive value, relative to the baseline.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_FontAscent(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the offset from the baseline to the bottom of the font
|
||||
This is a negative value, relative to the baseline.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_FontDescent(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the recommended spacing between lines of text for this font */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_FontLineSkip(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the number of faces of the font */
|
||||
extern DECLSPEC long SDLCALL RENPY_TTF_FontFaces(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the font face attributes, if any */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_FontFaceIsFixedWidth(RENPY_TTF_Font *font);
|
||||
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceFamilyName(RENPY_TTF_Font *font);
|
||||
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceStyleName(RENPY_TTF_Font *font);
|
||||
|
||||
/* Get the metrics (dimensions) of a glyph
|
||||
To understand what these metrics mean, here is a useful link:
|
||||
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_GlyphMetrics(RENPY_TTF_Font *font, Uint16 ch,
|
||||
int *minx, int *maxx,
|
||||
int *miny, int *maxy, int *advance);
|
||||
|
||||
/* Get the dimensions of a rendered string of text */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_SizeText(RENPY_TTF_Font *font, const char *text, int *w, int *h);
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUTF8(RENPY_TTF_Font *font, const char *text, int *w, int *h);
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUNICODE(RENPY_TTF_Font *font, const Uint16 *text, int *w, int *h);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given text at
|
||||
fast quality with the given font and color. The 0 pixel is the
|
||||
colorkey, giving a transparent background, and the 1 pixel is set
|
||||
to the text color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Solid(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Solid(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Solid(RENPY_TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given glyph at
|
||||
fast quality with the given font and color. The 0 pixel is the
|
||||
colorkey, giving a transparent background, and the 1 pixel is set
|
||||
to the text color. The glyph is rendered without any padding or
|
||||
centering in the X direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Solid(RENPY_TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given text at
|
||||
high quality with the given font and colors. The 0 pixel is background,
|
||||
while other pixels have varying degrees of the foreground color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Shaded(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg, SDL_Color bg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Shaded(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg, SDL_Color bg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Shaded(RENPY_TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg, SDL_Color bg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given glyph at
|
||||
high quality with the given font and colors. The 0 pixel is background,
|
||||
while other pixels have varying degrees of the foreground color.
|
||||
The glyph is rendered without any padding or centering in the X
|
||||
direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Shaded(RENPY_TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg, SDL_Color bg);
|
||||
|
||||
/* Create a 32-bit ARGB surface and render the given text at high quality,
|
||||
using alpha blending to dither the font with the given color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Blended(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Blended(RENPY_TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Blended(RENPY_TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg);
|
||||
|
||||
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
|
||||
using alpha blending to dither the font with the given color.
|
||||
The glyph is rendered without any padding or centering in the X
|
||||
direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Blended(RENPY_TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg);
|
||||
|
||||
/* For compatibility with previous versions, here are the old functions */
|
||||
#define RENPY_TTF_RenderText(font, text, fg, bg) \
|
||||
RENPY_TTF_RenderText_Shaded(font, text, fg, bg)
|
||||
#define RENPY_TTF_RenderUTF8(font, text, fg, bg) \
|
||||
RENPY_TTF_RenderUTF8_Shaded(font, text, fg, bg)
|
||||
#define RENPY_TTF_RenderUNICODE(font, text, fg, bg) \
|
||||
RENPY_TTF_RenderUNICODE_Shaded(font, text, fg, bg)
|
||||
|
||||
/* Close an opened font file */
|
||||
extern DECLSPEC void SDLCALL RENPY_TTF_CloseFont(RENPY_TTF_Font *font);
|
||||
|
||||
/* De-initialize the TTF engine */
|
||||
extern DECLSPEC void SDLCALL RENPY_TTF_Quit(void);
|
||||
|
||||
/* Check if the TTF engine is initialized */
|
||||
extern DECLSPEC int SDLCALL RENPY_TTF_WasInit(void);
|
||||
|
||||
extern DECLSPEC void SDLCALL RENPY_TTF_SetExpand(RENPY_TTF_Font *, float);
|
||||
|
||||
/* We'll use SDL for reporting errors */
|
||||
#define RENPY_TTF_SetError SDL_SetError
|
||||
#define RENPY_TTF_GetError SDL_GetError
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include "close_code.h"
|
||||
|
||||
#endif /* _RENPY_TTF_H */
|
||||
@@ -1,451 +0,0 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
/*
|
||||
* Updated by Tom to separate from pygame.
|
||||
*/
|
||||
|
||||
#define RAISE(x,y) (PyErr_SetString((x), (y)), (PyObject*)NULL)
|
||||
|
||||
/*
|
||||
* SDL_RWops support for python objects
|
||||
*/
|
||||
#include <Python.h>
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject* read;
|
||||
PyObject* write;
|
||||
PyObject* seek;
|
||||
PyObject* tell;
|
||||
PyObject* close;
|
||||
#ifdef WITH_THREAD
|
||||
PyThreadState* thread;
|
||||
#endif
|
||||
}RWHelper;
|
||||
|
||||
|
||||
static int rw_seek(SDL_RWops* context, int offset, int whence);
|
||||
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum);
|
||||
static int rw_write(SDL_RWops* context, const void* ptr, int size, int maxnum);
|
||||
static int rw_close(SDL_RWops* context);
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
static int rw_seek_th(SDL_RWops* context, int offset, int whence);
|
||||
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum);
|
||||
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int maxnum);
|
||||
static int rw_close_th(SDL_RWops* context);
|
||||
#endif
|
||||
|
||||
|
||||
static SDL_RWops* get_standard_rwop(PyObject* obj)
|
||||
{
|
||||
if(PyString_Check(obj) || PyUnicode_Check(obj))
|
||||
{
|
||||
int result;
|
||||
char* name;
|
||||
PyObject* tuple = PyTuple_New(1);
|
||||
PyTuple_SET_ITEM(tuple, 0, obj);
|
||||
Py_INCREF(obj);
|
||||
if(!tuple) return NULL;
|
||||
result = PyArg_ParseTuple(tuple, "s", &name);
|
||||
Py_DECREF(tuple);
|
||||
if(!result)
|
||||
return NULL;
|
||||
return SDL_RWFromFile(name, "rb");
|
||||
}
|
||||
// else if(PyFile_Check(obj))
|
||||
// return SDL_RWFromFP(PyFile_AsFile(obj), 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fetch_object_methods(RWHelper* helper, PyObject* obj)
|
||||
{
|
||||
helper->read = helper->write = helper->seek =
|
||||
helper->tell = helper->close = NULL;
|
||||
if(PyObject_HasAttrString(obj, "read"))
|
||||
{
|
||||
helper->read = PyObject_GetAttrString(obj, "read");
|
||||
if(helper->read && !PyCallable_Check(helper->read))
|
||||
helper->read = NULL;
|
||||
}
|
||||
if(PyObject_HasAttrString(obj, "write"))
|
||||
{
|
||||
helper->write = PyObject_GetAttrString(obj, "write");
|
||||
if(helper->write && !PyCallable_Check(helper->write))
|
||||
helper->write = NULL;
|
||||
}
|
||||
if(PyObject_HasAttrString(obj, "seek"))
|
||||
{
|
||||
helper->seek = PyObject_GetAttrString(obj, "seek");
|
||||
if(helper->seek && !PyCallable_Check(helper->seek))
|
||||
helper->seek = NULL;
|
||||
}
|
||||
if(PyObject_HasAttrString(obj, "tell"))
|
||||
{
|
||||
helper->tell = PyObject_GetAttrString(obj, "tell");
|
||||
if(helper->tell && !PyCallable_Check(helper->tell))
|
||||
helper->tell = NULL;
|
||||
}
|
||||
if(PyObject_HasAttrString(obj, "close"))
|
||||
{
|
||||
helper->close = PyObject_GetAttrString(obj, "close");
|
||||
if(helper->close && !PyCallable_Check(helper->close))
|
||||
helper->close = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SDL_RWops* RWopsFromPython(PyObject* obj)
|
||||
{
|
||||
SDL_RWops* rw;
|
||||
RWHelper* helper;
|
||||
|
||||
if(!obj)
|
||||
return (SDL_RWops*) RAISE(PyExc_TypeError, "Invalid filetype object");
|
||||
rw = get_standard_rwop(obj);
|
||||
if(rw) return rw;
|
||||
|
||||
|
||||
helper = PyMem_New(RWHelper, 1);
|
||||
fetch_object_methods(helper, obj);
|
||||
|
||||
rw = SDL_AllocRW();
|
||||
rw->hidden.unknown.data1 = (void*)helper;
|
||||
rw->seek = rw_seek;
|
||||
rw->read = rw_read;
|
||||
rw->write = rw_write;
|
||||
rw->close = rw_close;
|
||||
|
||||
return rw;
|
||||
}
|
||||
|
||||
|
||||
static int RWopsCheckPython(SDL_RWops* rw)
|
||||
{
|
||||
return rw->close == rw_close;
|
||||
}
|
||||
|
||||
|
||||
static int rw_seek(SDL_RWops* context, int offset, int whence)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval;
|
||||
|
||||
if(!helper->seek || !helper->tell)
|
||||
return -1;
|
||||
|
||||
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
|
||||
{
|
||||
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
|
||||
if(!result)
|
||||
return -1;
|
||||
Py_DECREF(result);
|
||||
}
|
||||
|
||||
result = PyObject_CallFunction(helper->tell, NULL);
|
||||
if(!result)
|
||||
return -1;
|
||||
|
||||
retval = PyInt_AsLong(result);
|
||||
Py_DECREF(result);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval;
|
||||
|
||||
if(!helper->read)
|
||||
return -1;
|
||||
|
||||
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
|
||||
if(!result)
|
||||
return -1;
|
||||
|
||||
if(!PyString_Check(result))
|
||||
{
|
||||
Py_DECREF(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval = PyString_GET_SIZE(result);
|
||||
memcpy(ptr, PyString_AsString(result), retval);
|
||||
retval /= size;
|
||||
|
||||
Py_DECREF(result);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int rw_write(SDL_RWops* context, const void* ptr, int size, int num)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
|
||||
if(!helper->write)
|
||||
return -1;
|
||||
|
||||
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
|
||||
if(!result)
|
||||
return -1;
|
||||
|
||||
Py_DECREF(result);
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
static int rw_close(SDL_RWops* context)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval = 0;
|
||||
|
||||
if(helper->close)
|
||||
{
|
||||
result = PyObject_CallFunction(helper->close, NULL);
|
||||
if(result)
|
||||
retval = -1;
|
||||
Py_XDECREF(result);
|
||||
}
|
||||
|
||||
Py_XDECREF(helper->seek);
|
||||
Py_XDECREF(helper->tell);
|
||||
Py_XDECREF(helper->write);
|
||||
Py_XDECREF(helper->read);
|
||||
Py_XDECREF(helper->close);
|
||||
PyMem_Del(helper);
|
||||
SDL_FreeRW(context);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj)
|
||||
{
|
||||
SDL_RWops* rw;
|
||||
RWHelper* helper;
|
||||
PyInterpreterState* interp;
|
||||
PyThreadState* thread;
|
||||
|
||||
if(!obj)
|
||||
return (SDL_RWops*)RAISE(PyExc_TypeError, "Invalid filetype object");
|
||||
|
||||
rw = get_standard_rwop(obj);
|
||||
if(rw)
|
||||
return rw;
|
||||
|
||||
#ifndef WITH_THREAD
|
||||
return (SDL_RWops*)RAISE(PyExc_NotImplementedError, "Python built without thread support");
|
||||
#else
|
||||
helper = PyMem_New(RWHelper, 1);
|
||||
fetch_object_methods(helper, obj);
|
||||
|
||||
rw = SDL_AllocRW();
|
||||
rw->hidden.unknown.data1 = (void*)helper;
|
||||
rw->seek = rw_seek_th;
|
||||
rw->read = rw_read_th;
|
||||
rw->write = rw_write_th;
|
||||
rw->close = rw_close_th;
|
||||
|
||||
PyEval_InitThreads();
|
||||
thread = PyThreadState_Get();
|
||||
interp = thread->interp;
|
||||
helper->thread = PyThreadState_New(interp);
|
||||
|
||||
return rw;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int RWopsCheckPythonThreaded(SDL_RWops* rw)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
return rw->close == rw_close_th;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
static int rw_seek_th(SDL_RWops* context, int offset, int whence)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval;
|
||||
PyThreadState* oldstate;
|
||||
|
||||
if(!helper->seek || !helper->tell)
|
||||
return -1;
|
||||
|
||||
PyEval_AcquireLock();
|
||||
oldstate = PyThreadState_Swap(helper->thread);
|
||||
|
||||
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
|
||||
{
|
||||
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
|
||||
if(!result) {
|
||||
PyErr_Clear();
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(result);
|
||||
}
|
||||
|
||||
result = PyObject_CallFunction(helper->tell, NULL);
|
||||
if(!result) {
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
retval = PyInt_AsLong(result);
|
||||
Py_DECREF(result);
|
||||
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval;
|
||||
PyThreadState* oldstate;
|
||||
|
||||
if(!helper->read)
|
||||
return -1;
|
||||
|
||||
PyEval_AcquireLock();
|
||||
oldstate = PyThreadState_Swap(helper->thread);
|
||||
|
||||
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
|
||||
if(!result) {
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if(!PyString_Check(result))
|
||||
{
|
||||
Py_DECREF(result);
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval = PyString_GET_SIZE(result);
|
||||
memcpy(ptr, PyString_AsString(result), retval);
|
||||
retval /= size;
|
||||
|
||||
Py_DECREF(result);
|
||||
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int num)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
PyThreadState* oldstate;
|
||||
|
||||
if(!helper->write)
|
||||
return -1;
|
||||
|
||||
PyEval_AcquireLock();
|
||||
oldstate = PyThreadState_Swap(helper->thread);
|
||||
|
||||
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
|
||||
if(!result) {
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(result);
|
||||
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
static int rw_close_th(SDL_RWops* context)
|
||||
{
|
||||
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
|
||||
PyObject* result;
|
||||
int retval = 0;
|
||||
PyThreadState* oldstate;
|
||||
|
||||
PyEval_AcquireLock();
|
||||
oldstate = PyThreadState_Swap(helper->thread);
|
||||
|
||||
if(helper->close)
|
||||
{
|
||||
result = PyObject_CallFunction(helper->close, NULL);
|
||||
if(result)
|
||||
retval = -1;
|
||||
Py_XDECREF(result);
|
||||
}
|
||||
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyThreadState_Clear(helper->thread);
|
||||
PyThreadState_Delete(helper->thread);
|
||||
|
||||
Py_XDECREF(helper->seek);
|
||||
Py_XDECREF(helper->tell);
|
||||
Py_XDECREF(helper->write);
|
||||
Py_XDECREF(helper->read);
|
||||
Py_XDECREF(helper->close);
|
||||
PyMem_Del(helper);
|
||||
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
SDL_FreeRW(context);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+17
-15
@@ -37,6 +37,7 @@ library("png")
|
||||
library("avformat")
|
||||
library("avcodec")
|
||||
library("avutil")
|
||||
has_avresample = library("avresample", optional=True)
|
||||
has_swscale = library("swscale", optional=True)
|
||||
library("freetype")
|
||||
has_fribidi = library("fribidi", optional=True)
|
||||
@@ -46,7 +47,7 @@ has_libglew32 = library("glew32", optional=True)
|
||||
has_angle = windows and library("EGL", optional=True) and library("GLESv2", optional=True)
|
||||
|
||||
if android:
|
||||
sdl = [ 'sdl', 'GLESv1_CM', 'log' ]
|
||||
sdl = [ 'sdl', 'GLESv2', 'log' ]
|
||||
else:
|
||||
sdl = [ 'SDL' ]
|
||||
|
||||
@@ -54,15 +55,9 @@ else:
|
||||
# Modules directory.
|
||||
cython(
|
||||
"_renpy",
|
||||
[ "IMG_savepng.c", "core.c", "rwobject.c", "subpixel.c"],
|
||||
[ "IMG_savepng.c", "core.c", "subpixel.c"],
|
||||
sdl + [ 'png', 'z', 'm' ])
|
||||
|
||||
cmodule(
|
||||
"_renpy_font",
|
||||
[ "renpy_ttf.c", "renpy_font.c"],
|
||||
sdl + [ 'freetype', 'z', 'm' ],
|
||||
)
|
||||
|
||||
if has_fribidi and not android:
|
||||
cython(
|
||||
"_renpybidi",
|
||||
@@ -75,22 +70,29 @@ pymodule("pysdlsound.__init__")
|
||||
if not android:
|
||||
|
||||
sound = [ "avformat", "avcodec", "avutil", "z" ]
|
||||
macros = [ ]
|
||||
|
||||
if has_avresample:
|
||||
sound.insert(0, "avresample")
|
||||
macros.append(("HAS_RESAMPLE", 1))
|
||||
|
||||
if has_swscale:
|
||||
sound.insert(0, "swscale")
|
||||
|
||||
|
||||
cython(
|
||||
"pysdlsound.sound",
|
||||
[ "pss.c", "rwobject.c", "ffdecode.c" ],
|
||||
libs = sdl + sound)
|
||||
[ "pss.c", "ffdecode.c" ],
|
||||
libs = sdl + sound,
|
||||
define_macros=macros)
|
||||
|
||||
|
||||
# Display.
|
||||
cython("renpy.display.render", libs=[ 'z', 'm' ])
|
||||
cython("renpy.display.accelerator", libs=[ 'z', 'm' ])
|
||||
cython("renpy.display.accelerator", libs=sdl + [ 'z', 'm' ])
|
||||
|
||||
# Gl.
|
||||
if android:
|
||||
glew_libs = [ 'GLESv1_CM', 'z', 'm' ]
|
||||
glew_libs = [ 'GLESv2', 'z', 'm' ]
|
||||
elif has_libglew:
|
||||
glew_libs = [ 'GLEW' ]
|
||||
else:
|
||||
@@ -98,9 +100,9 @@ else:
|
||||
|
||||
cython("renpy.gl.gldraw", libs=glew_libs )
|
||||
cython("renpy.gl.gltexture", libs=glew_libs)
|
||||
cython("renpy.gl.glenviron_shader", libs=glew_libs)
|
||||
cython("renpy.gl.glenviron_fixed", libs=glew_libs, compile_if=not android)
|
||||
cython("renpy.gl.glenviron_shader", libs=glew_libs, compile_if=not android)
|
||||
cython("renpy.gl.glenviron_limited", libs=glew_libs)
|
||||
cython("renpy.gl.glenviron_limited", libs=glew_libs, compile_if=not android)
|
||||
cython("renpy.gl.glrtt_copy", libs=glew_libs)
|
||||
cython("renpy.gl.glrtt_fbo", libs=glew_libs)
|
||||
|
||||
|
||||
+13
-12
@@ -109,10 +109,11 @@ void free_gsubtable(TTGSUBTable *table)
|
||||
TSingleSubstFormat *subt = lup[i].SubTable;
|
||||
for(j = 0; j < ls_cnt; j++)
|
||||
{
|
||||
if(subt[j].Coverage.CoverageFormat == 1)
|
||||
if(subt[j].Coverage.CoverageFormat == 1) {
|
||||
free(subt[j].Coverage.GlyphArray);
|
||||
else if(subt[j].Coverage.CoverageFormat == 2)
|
||||
free(subt[j].Coverage.RangeRecord);
|
||||
} else if(subt[j].Coverage.CoverageFormat == 2) {
|
||||
free(subt[j].Coverage.RangeRecord);
|
||||
}
|
||||
if(subt[j].SubstFormat == 2)
|
||||
free(subt[j].Substitute);
|
||||
}
|
||||
@@ -267,7 +268,7 @@ void ParseScriptList(TTGSUBTable *table, FT_Bytes raw, TScriptList *rec)
|
||||
rec->ScriptRecord = NULL;
|
||||
return;
|
||||
}
|
||||
rec->ScriptRecord = malloc(sizeof(TScriptRecord) * rec->ScriptCount);
|
||||
rec->ScriptRecord = calloc(rec->ScriptCount, sizeof(TScriptRecord));
|
||||
for(i = 0; i < rec->ScriptCount; i++)
|
||||
{
|
||||
rec->ScriptRecord[i].ScriptTag = GetUInt32(&sp);
|
||||
@@ -287,7 +288,7 @@ void ParseScript(TTGSUBTable *table, FT_Bytes raw, TScript *rec)
|
||||
rec->LangSysRecord = NULL;
|
||||
return;
|
||||
}
|
||||
rec->LangSysRecord = malloc(sizeof(TLangSysRecord) * rec->LangSysCount);
|
||||
rec->LangSysRecord = calloc(rec->LangSysCount, sizeof(TLangSysRecord));
|
||||
for(i = 0; i < rec->LangSysCount; i++)
|
||||
{
|
||||
rec->LangSysRecord[i].LangSysTag = GetUInt32(&sp);
|
||||
@@ -317,7 +318,7 @@ void ParseFeatureList(TTGSUBTable *table, FT_Bytes raw, TFeatureList *rec)
|
||||
rec->FeatureRecord = NULL;
|
||||
return;
|
||||
}
|
||||
rec->FeatureRecord = malloc(sizeof(TFeatureRecord) * rec->FeatureCount);
|
||||
rec->FeatureRecord = calloc(rec->FeatureCount, sizeof(TFeatureRecord));
|
||||
for(i = 0; i < rec->FeatureCount; i++)
|
||||
{
|
||||
rec->FeatureRecord[i].FeatureTag = GetUInt32(&sp);
|
||||
@@ -336,7 +337,7 @@ void ParseFeature(TTGSUBTable *table, FT_Bytes raw, TFeature *rec)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rec->LookupListIndex = malloc(sizeof(uint16_t) * rec->LookupCount);
|
||||
rec->LookupListIndex = calloc(rec->LookupCount, sizeof(uint16_t));
|
||||
for(i = 0;i < rec->LookupCount; i++)
|
||||
{
|
||||
rec->LookupListIndex[i] = GetUInt16(&sp);
|
||||
@@ -353,7 +354,7 @@ void ParseLookupList(TTGSUBTable *table, FT_Bytes raw, TLookupList *rec)
|
||||
rec->Lookup = NULL;
|
||||
return;
|
||||
}
|
||||
rec->Lookup = malloc(sizeof(TLookup) * rec->LookupCount);
|
||||
rec->Lookup = calloc(rec->LookupCount, sizeof(TLookup));
|
||||
for(i = 0; i < rec->LookupCount; i++)
|
||||
{
|
||||
uint16_t offset = GetUInt16(&sp);
|
||||
@@ -373,7 +374,7 @@ void ParseLookup(TTGSUBTable *table, FT_Bytes raw, TLookup *rec)
|
||||
rec->SubTable = NULL;
|
||||
return;
|
||||
}
|
||||
rec->SubTable = malloc(sizeof(TSingleSubstFormat) * rec->SubTableCount);
|
||||
rec->SubTable = calloc(rec->SubTableCount, sizeof(TSingleSubstFormat));
|
||||
if(rec->LookupType != 1)
|
||||
return;
|
||||
for(i = 0; i < rec->SubTableCount; i++)
|
||||
@@ -413,7 +414,7 @@ void ParseCoverageFormat1(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec
|
||||
rec->GlyphArray = NULL;
|
||||
return;
|
||||
}
|
||||
rec->GlyphArray = malloc(sizeof(uint16_t) * rec->GlyphCount);
|
||||
rec->GlyphArray = calloc(rec->GlyphCount, sizeof(uint16_t));
|
||||
for(i = 0; i < rec->GlyphCount; i++)
|
||||
{
|
||||
rec->GlyphArray[i] = GetUInt16(&sp);
|
||||
@@ -431,7 +432,7 @@ void ParseCoverageFormat2(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec
|
||||
rec->RangeRecord = NULL;
|
||||
return;
|
||||
}
|
||||
rec->RangeRecord = malloc(sizeof(TRangeRecord) * rec->RangeCount);
|
||||
rec->RangeRecord = calloc(rec->RangeCount, sizeof(TRangeRecord));
|
||||
for(i = 0; i < rec->RangeCount; i++)
|
||||
{
|
||||
rec->RangeRecord[i].Start = GetUInt16(&sp);
|
||||
@@ -481,7 +482,7 @@ void ParseSingleSubstFormat2(TTGSUBTable *table, FT_Bytes raw, TSingleSubstForma
|
||||
rec->Substitute = NULL;
|
||||
return;
|
||||
}
|
||||
rec->Substitute = malloc(sizeof(uint16_t) * rec->GlyphCount);
|
||||
rec->Substitute = calloc(rec->GlyphCount, sizeof(uint16_t));
|
||||
for(i = 0; i < rec->GlyphCount; i++)
|
||||
{
|
||||
rec->Substitute[i] = GetUInt16(&sp);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
#@PydevCodeAnalysisIgnore
|
||||
|
||||
# This file is part of Ren'Py. The license below applies to Ren'Py only.
|
||||
# Games and other projects that use Ren'Py may use a different license.
|
||||
|
||||
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
@@ -37,6 +41,26 @@ def path_to_common(renpy_base):
|
||||
def path_to_saves(gamedir):
|
||||
import renpy #@UnresolvedImport
|
||||
|
||||
# Android.
|
||||
if renpy.android:
|
||||
paths = [
|
||||
os.path.join(os.environ["ANDROID_OLD_PUBLIC"], "game/saves"),
|
||||
os.path.join(os.environ["ANDROID_PRIVATE"], "saves"),
|
||||
os.path.join(os.environ["ANDROID_PUBLIC"], "saves"),
|
||||
]
|
||||
|
||||
for rv in paths:
|
||||
if os.path.isdir(rv):
|
||||
break
|
||||
|
||||
print "Using savedir", rv
|
||||
|
||||
# We return the last path as the default.
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
# No save directory given.
|
||||
if not renpy.config.save_directory:
|
||||
return gamedir + "/saves"
|
||||
|
||||
@@ -54,10 +78,7 @@ def path_to_saves(gamedir):
|
||||
path = newpath
|
||||
|
||||
# Otherwise, put the saves in a platform-specific location.
|
||||
if renpy.android:
|
||||
return gamedir + "/saves"
|
||||
|
||||
elif renpy.macintosh:
|
||||
if renpy.macintosh:
|
||||
rv = "~/Library/RenPy/" + renpy.config.save_directory
|
||||
return os.path.expanduser(rv)
|
||||
|
||||
@@ -76,7 +97,7 @@ def path_to_saves(gamedir):
|
||||
# Returns the path to the Ren'Py base directory (containing common and
|
||||
# the launcher, usually.)
|
||||
def path_to_renpy_base():
|
||||
renpy_base = os.path.dirname(sys.argv[0])
|
||||
renpy_base = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
renpy_base = os.environ.get('RENPY_BASE', renpy_base)
|
||||
renpy_base = os.path.abspath(renpy_base)
|
||||
|
||||
@@ -107,7 +128,6 @@ if android:
|
||||
__main__.path_to_common = path_to_common
|
||||
__main__.path_to_saves = path_to_saves
|
||||
os.environ["RENPY_RENDERER"] = "gl"
|
||||
os.environ["RENPY_GL_ENVIRON"] = "limited"
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
+2
-2
@@ -34,9 +34,9 @@ except ImportError:
|
||||
# The tuple giving the version. This needs to be updated when
|
||||
# we bump the version.
|
||||
#
|
||||
# Be sure to change script_version in launcher/script_version.rpy.
|
||||
# Be sure to change script_version in launcher/game/script_version.rpy.
|
||||
# Be sure to change config.version in tutorial/game/options.rpy.
|
||||
version_tuple = (6, 15, 0, vc_version)
|
||||
version_tuple = (6, 15, 7, vc_version)
|
||||
|
||||
# A verbose string computed from that version.
|
||||
version = "Ren'Py " + ".".join(str(i) for i in version_tuple)
|
||||
|
||||
+12
-4
@@ -278,7 +278,7 @@ class Node(object):
|
||||
be executed after this one.
|
||||
"""
|
||||
|
||||
assert False, "Node subclass forgot to define execute."
|
||||
raise Exception("Node subclass forgot to define execute.")
|
||||
|
||||
def early_execute(self):
|
||||
"""
|
||||
@@ -852,7 +852,7 @@ class Transform(Node):
|
||||
setattr(renpy.store, self.varname, trans)
|
||||
|
||||
|
||||
def predict_imspec(imspec, scene=False):
|
||||
def predict_imspec(imspec, scene=False, atl=None):
|
||||
"""
|
||||
Call this to use the given callback to predict the image named
|
||||
in imspec.
|
||||
@@ -888,9 +888,17 @@ def predict_imspec(imspec, scene=False):
|
||||
renpy.game.context().images.predict_scene(layer)
|
||||
|
||||
renpy.game.context().images.predict_show(tag or name, layer)
|
||||
|
||||
if atl is not None:
|
||||
try:
|
||||
img = renpy.display.motion.ATLTransform(atl, child=img)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
renpy.display.predict.displayable(img)
|
||||
|
||||
|
||||
|
||||
def show_imspec(imspec, atl=None):
|
||||
|
||||
@@ -955,7 +963,7 @@ class Show(Node):
|
||||
show_imspec(self.imspec, atl=getattr(self, "atl", None))
|
||||
|
||||
def predict(self):
|
||||
predict_imspec(self.imspec)
|
||||
predict_imspec(self.imspec, atl=getattr(self, "atl", None))
|
||||
return [ self.next ]
|
||||
|
||||
|
||||
@@ -1001,7 +1009,7 @@ class Scene(Node):
|
||||
def predict(self):
|
||||
|
||||
if self.imspec:
|
||||
predict_imspec(self.imspec, scene=True)
|
||||
predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True)
|
||||
|
||||
return [ self.next ]
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ PROPERTIES = {
|
||||
"yzoom" : float,
|
||||
"zoom" : float,
|
||||
"alpha" : float,
|
||||
"additive" : float,
|
||||
"around" : (position, position),
|
||||
"alignaround" : (float, float),
|
||||
"angle" : float,
|
||||
|
||||
+36
-13
@@ -87,12 +87,15 @@ class NullFile(io.IOBase):
|
||||
raise IOError("Not implemented.")
|
||||
|
||||
def null_files():
|
||||
if sys.stderr.fileno() < 0:
|
||||
sys.stderr = NullFile()
|
||||
try:
|
||||
if sys.stderr.fileno() < 0:
|
||||
sys.stderr = NullFile()
|
||||
|
||||
if sys.stdout.fileno() < 0:
|
||||
sys.stdout = NullFile()
|
||||
except:
|
||||
pass
|
||||
|
||||
if sys.stdout.fileno() < 0:
|
||||
sys.stdout = NullFile()
|
||||
|
||||
null_files()
|
||||
|
||||
|
||||
@@ -250,6 +253,14 @@ this program do not contain : or ; in their names.
|
||||
renpy.config.basedir = basedir
|
||||
renpy.config.gamedir = gamedir
|
||||
renpy.config.args = [ ]
|
||||
|
||||
if renpy.android:
|
||||
renpy.config.logdir = os.environ['ANDROID_PUBLIC']
|
||||
else:
|
||||
renpy.config.logdir = basedir
|
||||
|
||||
if not os.path.exists(renpy.config.logdir):
|
||||
os.makedirs(renpy.config.logdir, 0777)
|
||||
|
||||
renpy.main.main()
|
||||
keep_running = False
|
||||
@@ -370,7 +381,11 @@ def open_error_file(fn, mode):
|
||||
was opened.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
try:
|
||||
f = file(os.path.join(renpy.config.logdir, fn), mode)
|
||||
return f, fn
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
f = file(fn, mode)
|
||||
@@ -378,6 +393,8 @@ def open_error_file(fn, mode):
|
||||
except:
|
||||
pass
|
||||
|
||||
import tempfile
|
||||
|
||||
fn = os.path.join(tempfile.gettempdir(), "renpy-" + fn)
|
||||
return file(fn, mode), fn
|
||||
|
||||
@@ -475,13 +492,16 @@ def report_exception(e, editor=True):
|
||||
return simple.decode("utf-8", "replace"), full.decode("utf-8", "replace"), traceback_fn
|
||||
|
||||
|
||||
old_memory = { }
|
||||
|
||||
def memory_profile():
|
||||
|
||||
print "Memory Profile"
|
||||
print
|
||||
print "Showing all objects in memory at program termination."
|
||||
print
|
||||
|
||||
"""
|
||||
Calling this function displays the change in the number of instances of
|
||||
each type of object.
|
||||
"""
|
||||
|
||||
print "- Memory Profile ---------------------------------------------------"
|
||||
|
||||
import gc
|
||||
gc.collect()
|
||||
|
||||
@@ -497,4 +517,7 @@ def memory_profile():
|
||||
results.sort()
|
||||
|
||||
for count, ty in results:
|
||||
print count, str(ty)
|
||||
diff = count - old_memory.get(ty, 0)
|
||||
old_memory[ty] = count
|
||||
if diff:
|
||||
print diff, ty
|
||||
|
||||
+11
-5
@@ -346,7 +346,7 @@ def display_say(
|
||||
checkpoint=True,
|
||||
ctc_timedpause=None,
|
||||
ctc_force=False):
|
||||
|
||||
|
||||
# If we're in fast skipping mode, don't bother with say
|
||||
# statements at all.
|
||||
if interact and renpy.config.skipping == "fast":
|
||||
@@ -768,8 +768,11 @@ class ADVCharacter(object):
|
||||
sub = renpy.substitutions.substitute
|
||||
|
||||
if who is not None:
|
||||
who_pattern = sub(self.who_prefix + "[[who]" + self.who_suffix)
|
||||
who = who_pattern.replace("[who]", sub(who))
|
||||
if renpy.config.new_substitutions:
|
||||
who_pattern = sub(self.who_prefix + "[[who]" + self.who_suffix)
|
||||
who = who_pattern.replace("[who]", sub(who))
|
||||
else:
|
||||
who = self.who_prefix + who + self.who_suffix
|
||||
|
||||
ctx = renpy.game.context()
|
||||
|
||||
@@ -778,8 +781,11 @@ class ADVCharacter(object):
|
||||
else:
|
||||
translate = True
|
||||
|
||||
what_pattern = sub(self.what_prefix + "[[what]" + self.what_suffix)
|
||||
what = what_pattern.replace("[what]", sub(what, translate=translate))
|
||||
if renpy.config.new_substitutions:
|
||||
what_pattern = sub(self.what_prefix + "[[what]" + self.what_suffix)
|
||||
what = what_pattern.replace("[what]", sub(what, translate=translate))
|
||||
else:
|
||||
what = self.what_prefix + what + self.what_suffix
|
||||
|
||||
# Run the add_function, to add this character to the
|
||||
# things like NVL-mode.
|
||||
|
||||
@@ -228,7 +228,7 @@ init -1500 python:
|
||||
return False
|
||||
elif renpy.context()._main_menu:
|
||||
return False
|
||||
elif persistent._file_page == "auto":
|
||||
elif (self.page or persistent._file_page) == "auto":
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -165,8 +165,16 @@ init -1500 python:
|
||||
Causes the game to begin skipping. If the game is in a menu
|
||||
context, then this returns to the game. Otherwise, it just
|
||||
enables skipping.
|
||||
|
||||
`fast`
|
||||
If True, skips directly to the next menu choice.
|
||||
"""
|
||||
|
||||
|
||||
fast = False
|
||||
|
||||
def __init__(self, fast=False):
|
||||
self.fast = fast
|
||||
|
||||
def __call__(self):
|
||||
if not self.get_sensitive():
|
||||
return
|
||||
@@ -174,7 +182,15 @@ init -1500 python:
|
||||
if renpy.context()._menu:
|
||||
renpy.jump("_return_skipping")
|
||||
else:
|
||||
config.skipping = not config.skipping
|
||||
|
||||
if not config.skipping:
|
||||
if self.fast:
|
||||
config.skipping = "fast"
|
||||
else:
|
||||
config.skipping = "slow"
|
||||
else:
|
||||
config.skipping = None
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
|
||||
@@ -283,6 +283,9 @@ init -1500 python in build:
|
||||
# Are we building Ren'Py?
|
||||
renpy = False
|
||||
|
||||
# Should we exclude empty directories from the zip and tar files?
|
||||
exclude_empty_directories = True
|
||||
|
||||
# This function is called by the json_dump command to dump the build data
|
||||
# into the json file.
|
||||
def dump():
|
||||
@@ -301,6 +304,8 @@ init -1500 python in build:
|
||||
rv["version"] = version or directory_name
|
||||
rv["display_name"] = display_name or executable_name
|
||||
|
||||
rv["exclude_empty_directories"] = exclude_empty_directories
|
||||
|
||||
rv["renpy"] = renpy
|
||||
|
||||
return rv
|
||||
|
||||
@@ -68,6 +68,11 @@ init -1900 python:
|
||||
global MoveTransition
|
||||
MoveTransition = OldMoveTransition
|
||||
|
||||
define.move_transitions = define.old_move_transitions
|
||||
|
||||
define.move_transitions("move", 0.5)
|
||||
define.move_transitions("ease", 0.5, _ease_time_warp, _ease_in_time_warp, _ease_out_time_warp)
|
||||
|
||||
if version <= (6, 14, 1):
|
||||
config.key_repeat = None
|
||||
|
||||
|
||||
@@ -97,7 +97,6 @@ init -1400 python:
|
||||
|
||||
# Ease images around. These are basically cosine-warped moves.
|
||||
def _ease_out_time_warp(x):
|
||||
print "ZZZ"
|
||||
import math
|
||||
return 1.0 - math.cos(x * math.pi / 2.0)
|
||||
|
||||
@@ -244,8 +243,78 @@ init -1400 python:
|
||||
for k, v in moves.iteritems():
|
||||
setattr(store, prefix + k, v)
|
||||
|
||||
def old_move_transitions(prefix, delay, time_warp=None, in_time_warp=None, out_time_warp=None, old=False, layers=[ 'master' ], **kwargs):
|
||||
moves = {
|
||||
"" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs)),
|
||||
|
||||
"inright" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((1.0, None, 0.0, None), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"inleft" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((0.0, None, 1.0, None), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"intop" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((None, 0.0, None, 1.0), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"inbottom" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((None, 1.0, None, 0.0), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"outright" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((1.0, None, 0.0, None), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outleft" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((0.0, None, 1.0, None), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outtop" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((None, 0.0, None, 1.0), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outbottom" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((None, 1.0, None, 0.0), time_warp=time_warp, **kwargs)),
|
||||
}
|
||||
|
||||
for k, v in moves.iteritems():
|
||||
setattr(store, prefix + k, v)
|
||||
|
||||
define.move_transitions = move_transitions
|
||||
define.old_move_transitions = old_move_transitions
|
||||
del move_transitions
|
||||
del old_move_transitions
|
||||
|
||||
define.move_transitions("move", 0.5)
|
||||
define.move_transitions("ease", 0.5, _ease_time_warp, _ease_in_time_warp, _ease_out_time_warp)
|
||||
|
||||
@@ -12,12 +12,12 @@ init -1500 python:
|
||||
"""
|
||||
The action returned by MusicRoom.Play when called with a file.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, mr, filename):
|
||||
self.mr = mr
|
||||
self.filename = filename
|
||||
self.selected = self.get_selected()
|
||||
|
||||
|
||||
def __call__(self):
|
||||
self.mr.play(self.filename, 0)
|
||||
|
||||
@@ -42,7 +42,9 @@ init -1500 python:
|
||||
order.
|
||||
"""
|
||||
|
||||
def __init__(self, channel="music", fadeout=0.0, fadein=0.0):
|
||||
loop = False
|
||||
|
||||
def __init__(self, channel="music", fadeout=0.0, fadein=0.0, loop=False):
|
||||
"""
|
||||
`channel`
|
||||
The channel that this music room will operate on.
|
||||
@@ -54,6 +56,10 @@ init -1500 python:
|
||||
`fadein`
|
||||
The number of seconds it takes to fade in the new
|
||||
music when changing tracks.
|
||||
|
||||
`loop`
|
||||
If true, a music track will loop once played. If False, it
|
||||
will advance to the next track.
|
||||
"""
|
||||
|
||||
self.channel = channel
|
||||
@@ -71,6 +77,8 @@ init -1500 python:
|
||||
# The set of songs that are always unlocked.
|
||||
self.always_unlocked = set()
|
||||
|
||||
# Should we loop rather than advancing to the next track?
|
||||
self.loop = loop
|
||||
|
||||
def add(self, filename, always_unlocked=False):
|
||||
"""
|
||||
@@ -144,7 +152,10 @@ init -1500 python:
|
||||
|
||||
idx = (idx + offset) % len(playlist)
|
||||
|
||||
playlist = playlist[idx:] + playlist[:idx]
|
||||
if self.loop:
|
||||
playlist = [ playlist[idx] ]
|
||||
else:
|
||||
playlist = playlist[idx:] + playlist[:idx]
|
||||
|
||||
renpy.music.play(playlist, channel=self.channel, fadeout=self.fadeout, fadein=self.fadein)
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ init -1900 python:
|
||||
|
||||
# Prefixes to strip from automatic images.
|
||||
config.automatic_images_strip = [ ]
|
||||
|
||||
# The minimum number of components which the image name consists of is 2 by default.
|
||||
config.automatic_images_minimum_components = 2
|
||||
|
||||
|
||||
init 1900 python hide:
|
||||
@@ -86,8 +89,8 @@ init 1900 python hide:
|
||||
else:
|
||||
break
|
||||
|
||||
# Only names of 2 components or more.
|
||||
if len(name) < 2:
|
||||
# Only names of 2 components or more by default.
|
||||
if len(name) < config.automatic_images_minimum_components:
|
||||
continue
|
||||
|
||||
# Reject if it already exists.
|
||||
|
||||
@@ -86,6 +86,9 @@ init -1800 python hide:
|
||||
style.list_text = Style(style.default)
|
||||
|
||||
style.tile = Style(style.default, help='default style of tile')
|
||||
|
||||
# Not used - but some old games might customize it.
|
||||
style.error_root = Style(style.default)
|
||||
|
||||
# The base styles that can be customized by themes.
|
||||
|
||||
|
||||
@@ -886,7 +886,10 @@ init -1500 python in updater:
|
||||
|
||||
# Check the existence of the downloaded file.
|
||||
if not os.path.exists(new_fn):
|
||||
raise UpdateError("The update file was not downloaded.")
|
||||
if os.path.exists(new_fn + ".part"):
|
||||
os.rename(new_fn + ".part", new_fn)
|
||||
else:
|
||||
raise UpdateError("The update file was not downloaded.")
|
||||
|
||||
# Check that the downloaded file has the right digest.
|
||||
import hashlib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
|
||||
# See LICENSE.txt for license details.
|
||||
|
||||
init python hide:
|
||||
init python:
|
||||
|
||||
style.menu_button = Style(style.button, heavy=True, help='Buttons that are part of the main or game menus.')
|
||||
style.menu_button_text = Style(style.button_text, heavy=True, help='The label of buttons that are part of the main or game menus.')
|
||||
@@ -43,7 +43,6 @@ init python hide:
|
||||
style.yesno_button = Style(style.menu_button, heavy=True, help='A Yes/No button.')
|
||||
style.yesno_button_text = Style(style.menu_button_text, heavy=True, help='A Yes/No button label.')
|
||||
|
||||
|
||||
style.prefs_frame = Style(style.default, heavy=True, help='')
|
||||
style.prefs_pref_frame = Style(style.default, heavy=True, help='')
|
||||
style.prefs_pref_vbox = Style(style.thin_vbox, heavy=True, help='')
|
||||
@@ -323,15 +322,25 @@ init python hide:
|
||||
|
||||
store._selected_compat = [ ]
|
||||
|
||||
class _SelectedCompat(object):
|
||||
class _SelectedCompat(renpy.style.Style):
|
||||
|
||||
def __init__(self, target):
|
||||
self.__dict__["target"] = target
|
||||
self.__dict__["property_updates"] = [ ]
|
||||
|
||||
self.target = target
|
||||
self.property_updates = [ ]
|
||||
|
||||
super(_SelectedCompat, self).__init__(target)
|
||||
|
||||
store._selected_compat.append(self)
|
||||
|
||||
def __setattr__(self, k, v):
|
||||
def setattr(self, k, v):
|
||||
super(_SelectedCompat, self).setattr(k, v)
|
||||
|
||||
try:
|
||||
super(_SelectedCompat, self).setattr("selected_" + k, v)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.property_updates.append((k, v))
|
||||
|
||||
def apply(self):
|
||||
@@ -340,7 +349,8 @@ init python hide:
|
||||
setattr(target, "selected_" + k, v)
|
||||
|
||||
def clear(self):
|
||||
self.__dict__["property_updates"] = [ ]
|
||||
super(_SelectedCompat, self).clear()
|
||||
self.property_updates = [ ]
|
||||
|
||||
|
||||
style.selected_button = _SelectedCompat('button')
|
||||
@@ -350,8 +360,8 @@ init python hide:
|
||||
style.prefs_selected_button = _SelectedCompat('prefs_button')
|
||||
style.prefs_selected_button_text = _SelectedCompat('prefs_button_text')
|
||||
|
||||
def apply_selected_compat():
|
||||
def _apply_selected_compat():
|
||||
for scs in _selected_compat:
|
||||
scs.apply()
|
||||
|
||||
layout.compat_funcs.append(apply_selected_compat)
|
||||
layout.compat_funcs.append(_apply_selected_compat)
|
||||
|
||||
@@ -379,6 +379,7 @@ gamedir = None
|
||||
basedir = None
|
||||
renpy_base = None
|
||||
commondir = None
|
||||
logdir = None # Where log and error files go.
|
||||
|
||||
# Should we enable OpenGL mode?
|
||||
gl_enable = True
|
||||
|
||||
@@ -108,6 +108,7 @@ Solid = renpy.display.imagelike.Solid
|
||||
LiveComposite = renpy.display.layout.LiveComposite
|
||||
LiveCrop = renpy.display.layout.LiveCrop
|
||||
LiveTile = renpy.display.layout.LiveTile
|
||||
Flatten = renpy.display.layout.Flatten
|
||||
|
||||
Null = renpy.display.layout.Null
|
||||
Window = renpy.display.layout.Window
|
||||
|
||||
@@ -22,9 +22,43 @@
|
||||
|
||||
import renpy
|
||||
import math
|
||||
|
||||
from renpy.display.render cimport Render, Matrix2D, render
|
||||
|
||||
|
||||
################################################################################
|
||||
# Surface copying
|
||||
################################################################################
|
||||
|
||||
from pygame cimport *
|
||||
|
||||
def nogil_copy(src, dest):
|
||||
"""
|
||||
Does a gil-less blit of src to dest, with minimal locking.
|
||||
"""
|
||||
|
||||
cdef SDL_Surface *src_surf
|
||||
cdef SDL_Surface *dst_surf
|
||||
|
||||
src_surf = PySurface_AsSurface(src)
|
||||
dest_surf = PySurface_AsSurface(dest)
|
||||
|
||||
old_alpha = src_surf.flags & SDL_SRCALPHA
|
||||
|
||||
if old_alpha:
|
||||
SDL_SetAlpha(src_surf, 0, 255)
|
||||
|
||||
with nogil:
|
||||
SDL_BlitSurface(src_surf, NULL, dest_surf, NULL)
|
||||
|
||||
if old_alpha:
|
||||
SDL_SetAlpha(src_surf, SDL_SRCALPHA, 255)
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
# Transform render function
|
||||
################################################################################
|
||||
|
||||
cdef Matrix2D IDENTITY
|
||||
IDENTITY = renpy.display.render.IDENTITY
|
||||
|
||||
@@ -221,8 +255,6 @@ def transform_render(self, widtho, heighto, st, at):
|
||||
xo += width / 2.0
|
||||
yo += height / 2.0
|
||||
|
||||
alpha = state.alpha
|
||||
|
||||
rv = Render(width, height)
|
||||
|
||||
# Default case - no transformation matrix.
|
||||
@@ -245,7 +277,8 @@ def transform_render(self, widtho, heighto, st, at):
|
||||
-rydx / inv_det,
|
||||
rxdx / inv_det)
|
||||
|
||||
rv.alpha = alpha
|
||||
rv.alpha = state.alpha
|
||||
rv.over = 1.0 - state.additive
|
||||
rv.clipping = clipping
|
||||
|
||||
pos = (xo, yo)
|
||||
|
||||
@@ -1271,15 +1271,17 @@ class Bar(renpy.display.core.Displayable):
|
||||
super(Bar, self).focus(default)
|
||||
self.set_transform_event("hover")
|
||||
|
||||
run(self.hovered)
|
||||
if not default:
|
||||
run(self.hovered)
|
||||
|
||||
|
||||
def unfocus(self, default=False):
|
||||
super(Bar, self).unfocus()
|
||||
self.set_transform_event("idle")
|
||||
|
||||
run_unhovered(self.hovered)
|
||||
run(self.unhovered)
|
||||
if not default:
|
||||
run_unhovered(self.hovered)
|
||||
run(self.unhovered)
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
|
||||
|
||||
+60
-25
@@ -213,7 +213,7 @@ class Displayable(renpy.object.Object):
|
||||
in seconds.
|
||||
"""
|
||||
|
||||
assert False, "Draw not implemented."
|
||||
raise Exception("Render not implemented.")
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
"""
|
||||
@@ -1080,6 +1080,9 @@ class Interface(object):
|
||||
# Have we shown the window this interaction?
|
||||
self.shown_window = False
|
||||
|
||||
# Are we in fullscren mode?
|
||||
self.fullscreen = False
|
||||
|
||||
for layer in renpy.config.layers + renpy.config.top_layers:
|
||||
if layer in renpy.config.layer_clipping:
|
||||
x, y, w, h = renpy.config.layer_clipping[layer]
|
||||
@@ -1366,6 +1369,7 @@ class Interface(object):
|
||||
if s and (s.get_flags() & pygame.FULLSCREEN):
|
||||
fullscreen = False
|
||||
|
||||
old_fullscreen = self.fullscreen
|
||||
self.fullscreen = fullscreen
|
||||
|
||||
if os.environ.get('RENPY_DISABLE_FULLSCREEN', False):
|
||||
@@ -1389,7 +1393,7 @@ class Interface(object):
|
||||
raise Exception("Could not set video mode.")
|
||||
|
||||
# Save the video size.
|
||||
if renpy.config.save_physical_size and not fullscreen:
|
||||
if renpy.config.save_physical_size and not fullscreen and not old_fullscreen:
|
||||
renpy.game.preferences.physical_size = renpy.display.draw.get_physical_size()
|
||||
|
||||
if android:
|
||||
@@ -1408,7 +1412,7 @@ class Interface(object):
|
||||
self.restart_interaction = True
|
||||
|
||||
|
||||
def draw_screen(self, root_widget, fullscreen_video):
|
||||
def draw_screen(self, root_widget, fullscreen_video, draw):
|
||||
|
||||
surftree = renpy.display.render.render_screen(
|
||||
root_widget,
|
||||
@@ -1416,8 +1420,9 @@ class Interface(object):
|
||||
renpy.config.screen_height,
|
||||
)
|
||||
|
||||
renpy.display.draw.draw_screen(surftree, fullscreen_video)
|
||||
|
||||
if draw:
|
||||
renpy.display.draw.draw_screen(surftree, fullscreen_video)
|
||||
|
||||
renpy.display.render.mark_sweep()
|
||||
renpy.display.focus.take_focuses()
|
||||
|
||||
@@ -1789,6 +1794,26 @@ class Interface(object):
|
||||
if renpy.windows:
|
||||
self.display_reset = True
|
||||
self.set_mode(self.last_resize)
|
||||
|
||||
def enter_context(self):
|
||||
"""
|
||||
Called when we enter a new context.
|
||||
"""
|
||||
|
||||
# Stop ongoing transitions.
|
||||
self.ongoing_transition.clear()
|
||||
self.transition_from.clear()
|
||||
self.transition_time.clear()
|
||||
|
||||
def post_time_event(self):
|
||||
"""
|
||||
Posts a time_event object to the queue.
|
||||
"""
|
||||
|
||||
try:
|
||||
pygame.event.post(self.time_event)
|
||||
except:
|
||||
pass
|
||||
|
||||
def interact(self, clear=True, suppress_window=False, **kwargs):
|
||||
"""
|
||||
@@ -1805,6 +1830,7 @@ class Interface(object):
|
||||
raise Exception("Cannot start an interaction in the middle of an interaction, without creating a new context.")
|
||||
|
||||
context.interacting = True
|
||||
|
||||
|
||||
# Show a missing window.
|
||||
if not suppress_window:
|
||||
@@ -1868,7 +1894,7 @@ class Interface(object):
|
||||
@param suppress_overlay: This suppresses the display of the overlay.
|
||||
@param suppress_underlay: This suppresses the display of the underlay.
|
||||
"""
|
||||
|
||||
|
||||
self.roll_forward = roll_forward
|
||||
self.show_mouse = show_mouse
|
||||
|
||||
@@ -1940,10 +1966,7 @@ class Interface(object):
|
||||
REDRAW))
|
||||
|
||||
# Add a single TIMEEVENT to the queue.
|
||||
try:
|
||||
pygame.event.post(self.time_event)
|
||||
except:
|
||||
pass
|
||||
self.post_time_event()
|
||||
|
||||
# Figure out the scene list we want to show.
|
||||
scene_lists = renpy.game.context().scene_lists
|
||||
@@ -2057,6 +2080,7 @@ class Interface(object):
|
||||
add_layer(root_widget, layer)
|
||||
|
||||
prediction_coroutine = renpy.display.predict.prediction_coroutine(root_widget)
|
||||
prediction_coroutine.send(None)
|
||||
|
||||
# Clean out the registered adjustments.
|
||||
renpy.display.behavior.adj_registered.clear()
|
||||
@@ -2101,6 +2125,9 @@ class Interface(object):
|
||||
# How long until we redraw.
|
||||
redraw_in = 3600
|
||||
|
||||
# Have we drawn a frame yet?
|
||||
video_frame_drawn = False
|
||||
|
||||
# This try block is used to force cleanup even on termination
|
||||
# caused by an exception propagating through this function.
|
||||
try:
|
||||
@@ -2135,7 +2162,7 @@ class Interface(object):
|
||||
if not self.interact_time:
|
||||
self.interact_time = self.frame_time
|
||||
|
||||
self.draw_screen(root_widget, fullscreen_video)
|
||||
self.draw_screen(root_widget, fullscreen_video, (not fullscreen_video) or video_frame_drawn)
|
||||
|
||||
if first_pass:
|
||||
scene_lists.set_times(self.interact_time)
|
||||
@@ -2152,7 +2179,6 @@ class Interface(object):
|
||||
if new_time - self.profile_time > .015:
|
||||
print "Profile: Redraw took %f seconds." % (new_time - self.frame_time)
|
||||
print "Profile: %f seconds to complete event." % (new_time - self.profile_time)
|
||||
|
||||
|
||||
if first_pass and self.last_event:
|
||||
x, y = renpy.display.draw.get_mouse_pos()
|
||||
@@ -2164,7 +2190,7 @@ class Interface(object):
|
||||
pygame.time.set_timer(REDRAW, 0)
|
||||
pygame.event.clear([REDRAW])
|
||||
old_redraw_time = None
|
||||
|
||||
|
||||
# Draw the mouse, if it needs drawing.
|
||||
renpy.display.draw.update_mouse()
|
||||
|
||||
@@ -2174,6 +2200,11 @@ class Interface(object):
|
||||
|
||||
# Determine if we need a redraw. (We want to run these
|
||||
# functions, so we put them first to prevent short-circuiting.)
|
||||
|
||||
if renpy.display.video.frequent():
|
||||
needs_redraw = True
|
||||
video_frame_drawn = True
|
||||
|
||||
needs_redraw = renpy.display.video.frequent() or needs_redraw
|
||||
needs_redraw = renpy.display.render.process_redraws() or needs_redraw
|
||||
|
||||
@@ -2190,7 +2221,10 @@ class Interface(object):
|
||||
redraw_in = time_left
|
||||
|
||||
if time_left <= 0:
|
||||
pygame.event.post(self.redraw_event)
|
||||
try:
|
||||
pygame.event.post(self.redraw_event)
|
||||
except:
|
||||
pass
|
||||
pygame.time.set_timer(REDRAW, 0)
|
||||
else:
|
||||
pygame.time.set_timer(REDRAW, max(int(time_left * 1000), 1))
|
||||
@@ -2211,23 +2245,27 @@ class Interface(object):
|
||||
if time_left <= 0:
|
||||
self.timeout_time = None
|
||||
pygame.time.set_timer(TIMEEVENT, 0)
|
||||
pygame.event.post(self.time_event)
|
||||
self.post_time_event()
|
||||
elif self.timeout_time != old_timeout_time:
|
||||
# Always set to at least 1ms.
|
||||
pygame.time.set_timer(TIMEEVENT, int(time_left * 1000 + 1))
|
||||
old_timeout_time = self.timeout_time
|
||||
|
||||
# Predict images, if we haven't done so already.
|
||||
while (prediction_coroutine is not None) \
|
||||
and not needs_redraw \
|
||||
and not self.event_peek() \
|
||||
and not renpy.audio.music.is_playing("movie"):
|
||||
while prediction_coroutine is not None:
|
||||
|
||||
result = prediction_coroutine.next()
|
||||
# Can we do expensive prediction?
|
||||
expensive_predict = not (needs_redraw or self.event_peek() or renpy.audio.music.is_playing("movie"))
|
||||
|
||||
result = prediction_coroutine.send(expensive_predict)
|
||||
|
||||
if not result:
|
||||
prediction_coroutine = None
|
||||
break
|
||||
|
||||
if not expensive_predict:
|
||||
break
|
||||
|
||||
# If we need to redraw again, do it if we don't have an
|
||||
# event going on.
|
||||
if needs_redraw and not self.event_peek():
|
||||
@@ -2368,10 +2406,8 @@ class Interface(object):
|
||||
# An ignored event can change the timeout. So we want to
|
||||
# process an TIMEEVENT to ensure that the timeout is
|
||||
# set correctly.
|
||||
try:
|
||||
pygame.event.post(self.time_event)
|
||||
except:
|
||||
pass
|
||||
self.post_time_event()
|
||||
|
||||
|
||||
# Check again after handling the event.
|
||||
needs_redraw |= renpy.display.render.process_redraws()
|
||||
@@ -2383,7 +2419,6 @@ class Interface(object):
|
||||
# transitions up to the next interaction.
|
||||
if trans_pause and rv:
|
||||
self.suppress_transition = True
|
||||
|
||||
|
||||
# But wait, there's more! The finally block runs some cleanup
|
||||
# after this.
|
||||
|
||||
@@ -463,9 +463,9 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
|
||||
new_x = par_x - self.grab_x + xo
|
||||
new_y = par_y - self.grab_y + yo
|
||||
new_x = max(new_x, 0)
|
||||
new_x = min(new_x, i.parent_width - i.w)
|
||||
new_x = min(new_x, int(i.parent_width - i.w))
|
||||
new_y = max(new_y, 0)
|
||||
new_y = min(new_y, i.parent_height - i.h)
|
||||
new_y = min(new_y, int(i.parent_height - i.h))
|
||||
|
||||
if i.drag_group is not None and i.drag_name is not None:
|
||||
i.drag_group.positions[i.drag_name] = (new_x, new_y)
|
||||
|
||||
@@ -294,11 +294,9 @@ def horiz_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
|
||||
# The right end of a is to the left of the left end of b.
|
||||
if ax0 <= ax1 <= bx0 <= bx1:
|
||||
return points_dist(ax1, ay1, bx0, by0, renpy.config.focus_crossrange_penalty, 1.0)
|
||||
|
||||
if bx0 <= bx1 <= ax0 <= ax1:
|
||||
else:
|
||||
return points_dist(ax0, ay0, bx1, by1, renpy.config.focus_crossrange_penalty, 1.0)
|
||||
|
||||
assert False
|
||||
|
||||
# This computes the distance between two vertical lines. (So the
|
||||
# distance is either hortizontal, or has a horizontal component to it.)
|
||||
@@ -316,13 +314,9 @@ def verti_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
|
||||
# The right end of a is to the left of the left end of b.
|
||||
if ay0 <= ay1 <= by0 <= by1:
|
||||
return points_dist(ax1, ay1, bx0, by0, 1.0, renpy.config.focus_crossrange_penalty)
|
||||
|
||||
if by0 <= by1 <= ay0 <= ay1:
|
||||
else:
|
||||
return points_dist(ax0, ay0, bx1, by1, 1.0, renpy.config.focus_crossrange_penalty)
|
||||
|
||||
assert False
|
||||
|
||||
|
||||
|
||||
# This focuses the widget that is nearest to the current widget. To
|
||||
# determine nearest, we compute points on the widgets using the
|
||||
|
||||
+65
-65
@@ -31,6 +31,7 @@ import cStringIO
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
# This is an entry in the image cache.
|
||||
class CacheEntry(object):
|
||||
|
||||
@@ -74,9 +75,12 @@ class Cache(object):
|
||||
# The total size of everything in the cache.
|
||||
self.total_cache_size = 0
|
||||
|
||||
# A lock that must be held when updating the above.
|
||||
# A lock that must be held when updating the cache.
|
||||
self.lock = threading.Condition()
|
||||
|
||||
# A lock that mist be held to notify the preload thread.
|
||||
self.preload_lock = threading.Condition()
|
||||
|
||||
# Is the preload_thread alive?
|
||||
self.keep_preloading = True
|
||||
|
||||
@@ -120,16 +124,16 @@ class Cache(object):
|
||||
if not self.preload_thread.isAlive():
|
||||
return
|
||||
|
||||
self.lock.acquire()
|
||||
self.keep_preloading = False
|
||||
self.lock.notify()
|
||||
self.lock.release()
|
||||
with self.preload_lock:
|
||||
self.keep_preloading = False
|
||||
self.preload_lock.notify()
|
||||
|
||||
self.preload_thread.join()
|
||||
|
||||
|
||||
# Clears out the cache.
|
||||
def clear(self):
|
||||
|
||||
self.lock.acquire()
|
||||
|
||||
self.preloads = [ ]
|
||||
@@ -179,25 +183,12 @@ class Cache(object):
|
||||
renpy.display.render.mutated_surface(surf)
|
||||
return surf
|
||||
|
||||
ce = None
|
||||
|
||||
# First try to grab the image out of the cache without locking it.
|
||||
if image in self.cache:
|
||||
ce = self.cache[image]
|
||||
ce = self.cache.get(image, None)
|
||||
|
||||
# Now, grab the cache and try again. This deals with the case where the image
|
||||
# was already in the middle of preloading.
|
||||
# Otherwise, we load the image ourselves.
|
||||
if ce is None:
|
||||
|
||||
self.lock.acquire()
|
||||
ce = self.cache.get(image, None)
|
||||
|
||||
if ce is not None:
|
||||
self.lock.release()
|
||||
|
||||
# Otherwise, we keep the lock, and load the image ourselves.
|
||||
if ce is None:
|
||||
|
||||
try:
|
||||
if image in self.pin_cache:
|
||||
surf = self.pin_cache[image]
|
||||
@@ -205,33 +196,34 @@ class Cache(object):
|
||||
surf = image.load()
|
||||
|
||||
except:
|
||||
self.lock.release()
|
||||
raise
|
||||
|
||||
with self.lock:
|
||||
|
||||
ce = CacheEntry(image, surf)
|
||||
|
||||
if image not in self.cache:
|
||||
self.total_cache_size += ce.size
|
||||
|
||||
ce = CacheEntry(image, surf)
|
||||
self.total_cache_size += ce.size
|
||||
self.cache[image] = ce
|
||||
self.cache[image] = ce
|
||||
|
||||
# Indicate that this surface had changed.
|
||||
renpy.display.render.mutated_surface(ce.surf)
|
||||
|
||||
if renpy.config.debug_image_cache:
|
||||
if predict:
|
||||
renpy.display.ic_log.write("Added %r (%.02f%%)", ce.what, 100.0 * self.total_cache_size / self.cache_limit)
|
||||
else:
|
||||
renpy.display.ic_log.write("Total Miss %r", ce.what)
|
||||
|
||||
renpy.display.draw.load_texture(ce.surf)
|
||||
|
||||
# Indicate that this surface had changed.
|
||||
renpy.display.render.mutated_surface(ce.surf)
|
||||
|
||||
if renpy.config.debug_image_cache:
|
||||
|
||||
if predict:
|
||||
renpy.display.ic_log.write("Added %r (%.02f%%)", ce.what, 100.0 * self.total_cache_size / self.cache_limit)
|
||||
else:
|
||||
renpy.display.ic_log.write("Total Miss %r", ce.what)
|
||||
|
||||
renpy.display.draw.load_texture(ce.surf)
|
||||
|
||||
self.lock.release()
|
||||
|
||||
# Move it into the current generation. This isn't protected by
|
||||
# a lock, so in certain circumstances we could have an
|
||||
# inaccurate size. But that's pretty unlikely, as the
|
||||
# preloading thread should never run at the same time as an
|
||||
# actual load from the normal thread.
|
||||
|
||||
# inaccurate size - but that will be cured at the end of the
|
||||
# current generation.
|
||||
|
||||
if ce.time != self.time:
|
||||
ce.time = self.time
|
||||
self.size_of_current_generation += ce.size
|
||||
@@ -302,31 +294,38 @@ class Cache(object):
|
||||
in_cache = True
|
||||
else:
|
||||
self.preloads.append(im)
|
||||
self.lock.notify()
|
||||
in_cache = False
|
||||
|
||||
if not in_cache:
|
||||
|
||||
with self.preload_lock:
|
||||
self.preload_lock.notify()
|
||||
|
||||
if in_cache and renpy.config.debug_image_cache:
|
||||
renpy.display.ic_log.write("Kept %r", im)
|
||||
|
||||
def end_prediction(self):
|
||||
|
||||
def start_prediction(self):
|
||||
"""
|
||||
Called on the end of prediction, to kick of the thread so cleanup
|
||||
can happen.
|
||||
Called at the start of prediction, to ensure the thread runs
|
||||
at least once to clean out the cache.
|
||||
"""
|
||||
|
||||
with self.lock:
|
||||
self.lock.notify()
|
||||
|
||||
with self.preload_lock:
|
||||
self.preload_lock.notify()
|
||||
|
||||
def preload_thread_main(self):
|
||||
|
||||
while self.keep_preloading:
|
||||
|
||||
self.lock.acquire()
|
||||
self.lock.wait()
|
||||
self.lock.release()
|
||||
self.preload_lock.acquire()
|
||||
self.preload_lock.wait()
|
||||
self.preload_lock.release()
|
||||
|
||||
while self.preloads and self.keep_preloading:
|
||||
|
||||
start = time.time()
|
||||
|
||||
# If the size of the current generation is bigger than the
|
||||
# total cache size, stop preloading.
|
||||
with self.lock:
|
||||
@@ -337,24 +336,24 @@ class Cache(object):
|
||||
if renpy.config.debug_image_cache:
|
||||
for i in self.preloads:
|
||||
renpy.display.ic_log.write("Overfull %r", i)
|
||||
|
||||
|
||||
self.preloads = [ ]
|
||||
|
||||
break
|
||||
|
||||
|
||||
try:
|
||||
image = self.preloads.pop(0)
|
||||
try:
|
||||
image = self.preloads.pop(0)
|
||||
|
||||
if image not in self.preload_blacklist:
|
||||
try:
|
||||
self.get(image, True)
|
||||
except:
|
||||
self.preload_blacklist.add(image)
|
||||
if image not in self.preload_blacklist:
|
||||
try:
|
||||
self.get(image, True)
|
||||
except:
|
||||
self.preload_blacklist.add(image)
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
self.cleanout()
|
||||
with self.lock:
|
||||
self.cleanout()
|
||||
|
||||
# If we have time, preload pinned images.
|
||||
if self.keep_preloading and not renpy.game.less_memory:
|
||||
@@ -459,9 +458,10 @@ class ImageBase(renpy.display.core.Displayable):
|
||||
would override this.
|
||||
"""
|
||||
|
||||
assert False
|
||||
raise Exception("load method not implemented.")
|
||||
|
||||
def render(self, w, h, st, at):
|
||||
|
||||
im = cache.get(self)
|
||||
texture = renpy.display.draw.load_texture(im)
|
||||
|
||||
|
||||
+42
-3
@@ -478,7 +478,7 @@ class MultiBox(Container):
|
||||
|
||||
if adjust_times:
|
||||
|
||||
it = renpy.game.interface.interact_time - st
|
||||
it = renpy.game.interface.interact_time
|
||||
|
||||
self.start_times = [ i or it for i in self.start_times ]
|
||||
self.anim_times = [ i or it for i in self.anim_times ]
|
||||
@@ -1328,7 +1328,6 @@ class Viewport(Container):
|
||||
value = max(cw - width, 0) * self.xoffset
|
||||
|
||||
self.xadjustment.value = value
|
||||
self.xoffset = None
|
||||
|
||||
if self.yoffset is not None:
|
||||
if isinstance(self.yoffset, int):
|
||||
@@ -1337,7 +1336,6 @@ class Viewport(Container):
|
||||
value = max(ch - height, 0) * self.yoffset
|
||||
|
||||
self.yadjustment.value = value
|
||||
self.yoffset = None
|
||||
|
||||
if self.edge_size and self.edge_last_st and (self.edge_xspeed or self.edge_yspeed):
|
||||
|
||||
@@ -1378,6 +1376,9 @@ class Viewport(Container):
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
|
||||
self.xoffset = None
|
||||
self.yoffset = None
|
||||
|
||||
rv = super(Viewport, self).event(ev, x, y, st)
|
||||
if rv is not None:
|
||||
return rv
|
||||
@@ -1741,3 +1742,41 @@ class LiveTile(Container):
|
||||
rv.blit(cr, (x, y), focus=False)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
class Flatten(Container):
|
||||
"""
|
||||
:doc: disp_imagelike
|
||||
|
||||
This flattens `child`, which may be made up of multiple textures, into
|
||||
a single texture.
|
||||
|
||||
Certain operations, like the alpha transform property, apply to every
|
||||
texture making up a displayable, which can yield incorrect results
|
||||
when the textures overlap on screen. Flatten creates a single texture
|
||||
from multiple textures, which can prevent this problem.
|
||||
|
||||
Flatten is a relatively expensive operation, and so should only be used
|
||||
when absolutely required.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, child, **properties):
|
||||
super(Flatten, self).__init__(**properties)
|
||||
|
||||
self.add(child)
|
||||
|
||||
def render(self, width, height, st, at):
|
||||
cr = renpy.display.render.render(self.child, width, height, st, at)
|
||||
cw, ch = cr.get_size()
|
||||
|
||||
tex = cr.render_to_texture(True)
|
||||
|
||||
rv = renpy.display.render.Render(cw, ch)
|
||||
rv.blit(tex, (0, 0))
|
||||
rv.depends_on(cr, focus=True)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
@@ -95,9 +95,11 @@ class TransformState(renpy.object.Object):
|
||||
default_xoffset = None
|
||||
default_yoffset = None
|
||||
transform_anchor = False
|
||||
additive = 0.0
|
||||
|
||||
def __init__(self): # W0231
|
||||
self.alpha = 1
|
||||
self.additive = 0.0
|
||||
self.rotate = None
|
||||
self.rotate_pad = True
|
||||
self.transform_anchor = False
|
||||
@@ -142,6 +144,7 @@ class TransformState(renpy.object.Object):
|
||||
def take_state(self, ts):
|
||||
|
||||
self.alpha = ts.alpha
|
||||
self.additive = ts.additive
|
||||
self.rotate = ts.rotate
|
||||
self.rotate_pad = ts.rotate_pad
|
||||
self.transform_anchor = ts.transform_anchor
|
||||
@@ -196,6 +199,7 @@ class TransformState(renpy.object.Object):
|
||||
rv[prop] = (old_value, new_value)
|
||||
|
||||
diff2("alpha", newts.alpha, self.alpha)
|
||||
diff2("additive", newts.additive, self.additive)
|
||||
diff2("rotate", newts.rotate, self.rotate)
|
||||
diff2("rotate_pad", newts.rotate_pad, self.rotate_pad)
|
||||
diff2("transform_anchor", newts.transform_anchor, self.transform_anchor)
|
||||
@@ -380,6 +384,7 @@ class Transform(Container):
|
||||
|
||||
# Proxying things over to our state.
|
||||
alpha = Proxy("alpha")
|
||||
additive = Proxy("additive")
|
||||
rotate = Proxy("rotate")
|
||||
rotate_pad = Proxy("rotate_pad")
|
||||
transform_anchor = Proxy("rotate_pad")
|
||||
@@ -514,7 +519,7 @@ class Transform(Container):
|
||||
raise Exception("Unknown transform property prefix: %r" % prefix)
|
||||
|
||||
if prop not in renpy.atl.PROPERTIES:
|
||||
raise Exception("Unknown transform property: %r")
|
||||
raise Exception("Unknown transform property: %r" % prop)
|
||||
|
||||
self.arguments[prefix][prop] = v
|
||||
|
||||
@@ -725,6 +730,9 @@ class Transform(Container):
|
||||
children = self.children
|
||||
offsets = self.offsets
|
||||
|
||||
if not offsets:
|
||||
return None
|
||||
|
||||
for i in xrange(len(self.children)-1, -1, -1):
|
||||
|
||||
d = children[i]
|
||||
|
||||
@@ -191,8 +191,6 @@ def OldMoveTransition(delay, old_widget=None, new_widget=None, factory=None, ent
|
||||
# for each layer.
|
||||
if new.layers:
|
||||
|
||||
assert old.layers
|
||||
|
||||
rv = renpy.display.layout.MultiBox(layout='fixed')
|
||||
rv.layers = { }
|
||||
|
||||
@@ -502,12 +500,8 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
|
||||
# for each layer.
|
||||
if new.layers:
|
||||
|
||||
assert old.layers
|
||||
|
||||
rv = renpy.display.layout.MultiBox(layout='fixed')
|
||||
|
||||
rv.layers = { }
|
||||
|
||||
for layer in renpy.config.layers:
|
||||
|
||||
f = new.layers[layer]
|
||||
@@ -518,7 +512,6 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
|
||||
|
||||
f = merge_slide(old.layers[layer], new.layers[layer])
|
||||
|
||||
rv.layers[layer] = f
|
||||
rv.add(f)
|
||||
|
||||
return rv
|
||||
@@ -632,7 +625,6 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
|
||||
layer = new.layer_name
|
||||
rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
|
||||
rv.append_scene_list(rv_sl)
|
||||
rv.layer_name = layer
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ class SpriteManager(renpy.display.core.Displayable):
|
||||
if self.dead_child:
|
||||
self.children = [ i for i in self.children if i.live ]
|
||||
|
||||
self.children.sort()
|
||||
self.children.sort(key=lambda sc:sc.zorder)
|
||||
|
||||
caches = [ ]
|
||||
|
||||
@@ -282,7 +282,7 @@ class SpriteManager(renpy.display.core.Displayable):
|
||||
cst = st - cache.st
|
||||
|
||||
cache.render = r = render(cache.child, width, height, cst, cst)
|
||||
cache.fast = (r.operation == BLIT) and (r.forward is None) and (r.alpha == 1.0)
|
||||
cache.fast = (r.operation == BLIT) and (r.forward is None) and (r.alpha == 1.0) and (r.over == 1.0)
|
||||
rv.depends_on(r)
|
||||
|
||||
caches.append(cache)
|
||||
|
||||
@@ -117,11 +117,7 @@ def copy_surface(surf, alpha=True):
|
||||
"""
|
||||
|
||||
rv = surface_unscaled(surf.get_size(), alpha)
|
||||
|
||||
renpy.display.render.blit_lock.acquire()
|
||||
rv.blit(surf, (0, 0))
|
||||
renpy.display.render.blit_lock.release()
|
||||
|
||||
renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
|
||||
return rv
|
||||
|
||||
copy_surface_unscaled = copy_surface
|
||||
@@ -131,7 +127,8 @@ copy_surface_unscaled = copy_surface
|
||||
|
||||
def load_image(f, filename):
|
||||
surf = pygame.image.load(f, renpy.exports.fsencode(filename))
|
||||
return copy_surface_unscaled(surf)
|
||||
rv = copy_surface_unscaled(surf)
|
||||
return rv
|
||||
|
||||
load_image_unscaled = load_image
|
||||
|
||||
|
||||
+23
-10
@@ -71,10 +71,23 @@ def prediction_coroutine(root_widget):
|
||||
The image prediction co-routine. This predicts the images that can
|
||||
be loaded in the near future, and passes them to the image cache's
|
||||
preload_image method to be queued up for loading.
|
||||
|
||||
The .send should be called with True to do a expensive prediction,
|
||||
and with False to either do an inexpensive prediction or no
|
||||
prediction at all.
|
||||
|
||||
Returns True if there's more predicting to be done, or False
|
||||
if there's no more predicting worth doing.
|
||||
"""
|
||||
|
||||
global predicting
|
||||
|
||||
|
||||
# Wait to be told to start.
|
||||
yield True
|
||||
|
||||
# Start the prediction thread (to clean out the cache).
|
||||
renpy.display.im.cache.start_prediction()
|
||||
|
||||
# Set up the image prediction method.
|
||||
global image
|
||||
image = renpy.display.im.cache.preload_image
|
||||
@@ -83,15 +96,15 @@ def prediction_coroutine(root_widget):
|
||||
# clicks.
|
||||
predicting = True
|
||||
|
||||
renpy.game.context().predict()
|
||||
|
||||
predicting = False
|
||||
yield True
|
||||
for _i in renpy.game.context().predict():
|
||||
|
||||
predicting = False
|
||||
yield True
|
||||
predicting = True
|
||||
|
||||
# If there's a parent context, predict we'll be returning to it
|
||||
# shortly. Otherwise, call the functions in
|
||||
# config.predict_callbacks.
|
||||
predicting = True
|
||||
|
||||
if len(renpy.game.contexts) >= 2:
|
||||
sls = renpy.game.contexts[-2].scene_lists
|
||||
@@ -109,7 +122,8 @@ def prediction_coroutine(root_widget):
|
||||
|
||||
predicting = False
|
||||
|
||||
yield True
|
||||
while not (yield True):
|
||||
continue
|
||||
|
||||
# Predict things (especially screens) that are reachable through
|
||||
# an action.
|
||||
@@ -124,7 +138,8 @@ def prediction_coroutine(root_widget):
|
||||
|
||||
# Predict the screens themselves.
|
||||
for name, args, kwargs in screens:
|
||||
yield True
|
||||
while not (yield True):
|
||||
continue
|
||||
|
||||
predicting = True
|
||||
|
||||
@@ -136,8 +151,6 @@ def prediction_coroutine(root_widget):
|
||||
renpy.display.ic_log.exception()
|
||||
|
||||
predicting = False
|
||||
|
||||
renpy.display.im.cache.end_prediction()
|
||||
|
||||
yield False
|
||||
|
||||
|
||||
@@ -50,10 +50,7 @@ def start(basedir, gamedir):
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if sys.argv[0].lower().endswith(".exe"):
|
||||
cmd = [sys.argv[0], "show", "presplash", fn]
|
||||
else:
|
||||
cmd = [sys.executable, "-OO", sys.argv[0], "show", "presplash", fn]
|
||||
cmd = [sys.executable, "-EOO", sys.argv[0], "show", "presplash", fn]
|
||||
|
||||
def fsencode(s):
|
||||
if isinstance(s, str):
|
||||
|
||||
@@ -24,6 +24,7 @@ cdef class Render:
|
||||
|
||||
cdef public Matrix2D forward, reverse
|
||||
cdef public double alpha
|
||||
cdef public double over
|
||||
|
||||
cdef public list focuses
|
||||
cdef public list pass_focuses
|
||||
|
||||
@@ -508,6 +508,10 @@ cdef class Render:
|
||||
|
||||
# This is used to adjust the alpha of children of this render.
|
||||
self.alpha = 1
|
||||
|
||||
# The over blending factor. When this is 1.0, we get fully additive
|
||||
# blending. When set to 0.0, we get additive blending.
|
||||
self.over = 1.0
|
||||
|
||||
# A list of focus regions in this displayable.
|
||||
self.focuses = None
|
||||
|
||||
@@ -249,10 +249,9 @@ def surface(w, h, alpha):
|
||||
|
||||
def copy_surface(surf):
|
||||
w, h = surf.get_size()
|
||||
|
||||
rv = surface(w, h, True)
|
||||
rv.blit(surf, (0, 0))
|
||||
|
||||
|
||||
renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
|
||||
return rv
|
||||
|
||||
def draw_special(what, dest, x, y):
|
||||
@@ -479,10 +478,10 @@ def draw(dest, clip, what, xo, yo, screen):
|
||||
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):
|
||||
if what.alpha != 1 or what.over != 1.0 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)
|
||||
what.alpha * what.over, what.forward, what.reverse)
|
||||
return
|
||||
|
||||
for child, cxo, cyo, _focus, _main in what.visible_children:
|
||||
@@ -630,7 +629,7 @@ def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
|
||||
child_forward = forward
|
||||
child_reverse = reverse
|
||||
|
||||
draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
|
||||
draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha * what.over, child_forward, child_reverse)
|
||||
|
||||
|
||||
|
||||
@@ -696,7 +695,7 @@ class SWDraw(object):
|
||||
self.fullscreen_surface = None
|
||||
|
||||
# Info.
|
||||
self.info = { "renderer" : "sw", "resizable" : False }
|
||||
self.info = { "renderer" : "sw", "resizable" : False, "additive" : False }
|
||||
|
||||
pygame.display.init()
|
||||
renpy.display.interface.post_init()
|
||||
|
||||
@@ -127,6 +127,10 @@ def launch_editor(filenames, line=1, transient=False):
|
||||
Causes the editor to be launched.
|
||||
"""
|
||||
|
||||
# On android, we will never be able to launch the editor.
|
||||
if renpy.android:
|
||||
return True
|
||||
|
||||
if editor is None:
|
||||
init()
|
||||
|
||||
|
||||
+14
-3
@@ -394,6 +394,9 @@ class Context(renpy.object.Object):
|
||||
|
||||
rv.runtime = self.runtime
|
||||
rv.info = self.info
|
||||
|
||||
rv.translate_language = self.translate_language
|
||||
rv.translate_identifier = self.translate_identifier
|
||||
|
||||
return rv
|
||||
|
||||
@@ -443,7 +446,11 @@ class Context(renpy.object.Object):
|
||||
|
||||
# We accept that sometimes prediction won't work.
|
||||
|
||||
self.images = old_images
|
||||
self.images = old_images
|
||||
|
||||
yield True
|
||||
|
||||
yield False
|
||||
|
||||
|
||||
def seen_current(self, ever):
|
||||
@@ -500,8 +507,12 @@ def run_context(top):
|
||||
label = None
|
||||
|
||||
context.run()
|
||||
|
||||
rv = renpy.store._return
|
||||
|
||||
context.pop_all_dynamic()
|
||||
break
|
||||
|
||||
return rv
|
||||
|
||||
except renpy.game.RestartContext as e:
|
||||
|
||||
@@ -525,5 +536,5 @@ def run_context(top):
|
||||
context.pop_all_dynamic()
|
||||
raise
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+37
-28
@@ -218,14 +218,15 @@ def copy_images(old, new):
|
||||
|
||||
def showing(name, layer='master'):
|
||||
"""
|
||||
This returns true if an image with the same tag as that found in
|
||||
the suppled image name is present on the given layer.
|
||||
|
||||
@param name may be a tuple of strings, or a single string. In the latter
|
||||
case, it is split on whitespace to make a tuple. The first element
|
||||
of the tuple is used as the image tag.
|
||||
|
||||
@param layer is the name of the layer.
|
||||
:doc: image_func
|
||||
|
||||
Returns true if an image with the same tag as `name` is showing on
|
||||
`layer`
|
||||
|
||||
`image`
|
||||
May be a string giving the image name or a tuple giving each
|
||||
component of the image name. It may also be a string giving
|
||||
only the image tag.
|
||||
"""
|
||||
|
||||
if not isinstance(name, tuple):
|
||||
@@ -397,24 +398,28 @@ def watch(expression, style='default', **properties):
|
||||
|
||||
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None): #@ReservedAssignment
|
||||
"""
|
||||
This pops up a window requesting that the user enter in some text.
|
||||
It returns the entered text.
|
||||
:doc: input
|
||||
|
||||
@param prompt: A prompt that is used to ask the user for the text.
|
||||
|
||||
@param default: A default for the text that this input can return.
|
||||
|
||||
@param length: If given, a limit to the amount of text that this
|
||||
function will return.
|
||||
|
||||
@param allow: If not None, then if an input character is not in this
|
||||
string, it is ignored.
|
||||
|
||||
@param exclude: If not None, then if an input character is in this
|
||||
set, it is ignored.
|
||||
|
||||
@param with_none: If True, performs a with None after the input. If None,
|
||||
takes the value from config.implicit_with_none.
|
||||
Calling this function pops up a window asking the player to enter some
|
||||
text. It returns the entered text.
|
||||
|
||||
`prompt`
|
||||
A string giving a prompt to display to the player.
|
||||
|
||||
`default`
|
||||
A string giving the initial text that will be edited by the player.
|
||||
|
||||
`allow`
|
||||
If not None, a string giving a list of characters that will
|
||||
be allowed in the text.
|
||||
|
||||
`exclude`
|
||||
If not None, if a character is present in this string, it is not
|
||||
allowed in the text.
|
||||
|
||||
`length`
|
||||
If not None, this must be an integer giving the maximum length
|
||||
of the input string.
|
||||
"""
|
||||
|
||||
renpy.exports.mode('input')
|
||||
@@ -1663,9 +1668,12 @@ def get_renderer_info():
|
||||
One of ``"gl"`` or ``"sw"``, corresponding to the OpenGL and
|
||||
software renderers, respectively.
|
||||
|
||||
``"resizable``
|
||||
``"resizable"``
|
||||
True if and only if the window is resizable.
|
||||
|
||||
``"additive"``
|
||||
True if and only if the renderer supports additive blending.
|
||||
|
||||
Other, renderer-specific, keys may also exist. The dictionary should
|
||||
be treated as immutable. This should only be called once the display
|
||||
has been started (that is, after the init code is finished).
|
||||
@@ -1819,13 +1827,14 @@ def set_physical_size(size):
|
||||
"""
|
||||
:doc: other
|
||||
|
||||
Attempts to set the size of the physical window to size. This has the
|
||||
side effect of taking the screen out of windowed mode.
|
||||
Attempts to set the size of the physical window to `size`. This has the
|
||||
side effect of taking the screen out of fullscreen mode.
|
||||
"""
|
||||
|
||||
renpy.game.preferences.fullscreen = False
|
||||
|
||||
if get_renderer_info()["resizable"]:
|
||||
renpy.display.draw.quit()
|
||||
renpy.display.interface.set_mode(size)
|
||||
|
||||
def fsencode(s):
|
||||
|
||||
+10
-5
@@ -315,6 +315,9 @@ def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment
|
||||
context = renpy.execution.Context(False, contexts[-1], clear=True)
|
||||
contexts.append(context)
|
||||
|
||||
if renpy.display.interface is not None:
|
||||
renpy.display.interface.enter_context()
|
||||
|
||||
try:
|
||||
|
||||
return callable(*args, **kwargs)
|
||||
@@ -346,6 +349,9 @@ def call_in_new_context(label, *args, **kwargs):
|
||||
|
||||
context = renpy.execution.Context(False, contexts[-1], clear=True)
|
||||
contexts.append(context)
|
||||
|
||||
if renpy.display.interface is not None:
|
||||
renpy.display.interface.enter_context()
|
||||
|
||||
if args:
|
||||
renpy.store._args = args
|
||||
@@ -360,11 +366,7 @@ def call_in_new_context(label, *args, **kwargs):
|
||||
try:
|
||||
|
||||
context.goto_label(label)
|
||||
renpy.execution.run_context(False)
|
||||
|
||||
rv = renpy.store._return #@UndefinedVariable
|
||||
|
||||
return rv
|
||||
return renpy.execution.run_context(False)
|
||||
|
||||
except renpy.game.JumpOutException, e:
|
||||
|
||||
@@ -399,6 +401,9 @@ def call_replay(label, scope={}):
|
||||
context = renpy.execution.Context(True)
|
||||
contexts.append(context)
|
||||
|
||||
if renpy.display.interface is not None:
|
||||
renpy.display.interface.enter_context()
|
||||
|
||||
for k, v in scope.iteritems():
|
||||
setattr(renpy.store, k, v)
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ cdef class GLDraw:
|
||||
cdef bint fast_dissolve
|
||||
cdef bint always_opaque
|
||||
cdef bint allow_fixed
|
||||
cdef tuple default_clip
|
||||
|
||||
cdef public tuple clip_rtt_box
|
||||
|
||||
@@ -43,6 +44,7 @@ cdef class GLDraw:
|
||||
double xo,
|
||||
double yo,
|
||||
double alpha,
|
||||
double over,
|
||||
render.Matrix2D reverse)
|
||||
|
||||
cdef class Environ:
|
||||
|
||||
+39
-27
@@ -111,7 +111,7 @@ cdef class GLDraw:
|
||||
self.redraw_period = .2
|
||||
|
||||
# Info.
|
||||
self.info = { "resizable" : True }
|
||||
self.info = { "resizable" : True, "additive" : True }
|
||||
|
||||
if not ANGLE:
|
||||
self.info["renderer"] = "gl"
|
||||
@@ -206,19 +206,21 @@ cdef class GLDraw:
|
||||
# Handle swap control.
|
||||
vsync = int(os.environ.get("RENPY_GL_VSYNC", "1"))
|
||||
|
||||
# Switch the
|
||||
IF not ANGLE:
|
||||
if ANGLE:
|
||||
opengl = 0
|
||||
resizable = 0
|
||||
elif renpy.android:
|
||||
opengl = pygame.OPENGL
|
||||
resizable = 0
|
||||
else:
|
||||
opengl = pygame.OPENGL
|
||||
pygame.display.gl_set_attribute(pygame.GL_SWAP_CONTROL, vsync)
|
||||
pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8)
|
||||
ELSE:
|
||||
opengl = 0
|
||||
# EGL automatically handles vsync for us.
|
||||
|
||||
if renpy.config.gl_resize:
|
||||
resizable = pygame.RESIZABLE
|
||||
else:
|
||||
resizable = 0
|
||||
if renpy.config.gl_resize:
|
||||
resizable = pygame.RESIZABLE
|
||||
else:
|
||||
resizable = 0
|
||||
|
||||
try:
|
||||
if fullscreen:
|
||||
@@ -382,12 +384,13 @@ cdef class GLDraw:
|
||||
return False
|
||||
|
||||
if ANGLE:
|
||||
gltexture.use_angle()
|
||||
elif version.startswith("OpenGL ES"):
|
||||
gltexture.use_gles()
|
||||
|
||||
elif renpy.android:
|
||||
self.redraw_period = 1.0
|
||||
self.always_opaque = True
|
||||
|
||||
gltexture.use_gles()
|
||||
|
||||
else:
|
||||
gltexture.use_gl()
|
||||
|
||||
@@ -433,7 +436,7 @@ cdef class GLDraw:
|
||||
|
||||
# Pick a texture environment subsystem.
|
||||
|
||||
if ANGLE or (allow_shader and use_subsystem(
|
||||
if ANGLE or renpy.android or (allow_shader and use_subsystem(
|
||||
glenviron_shader,
|
||||
"RENPY_GL_ENVIRON",
|
||||
"shader",
|
||||
@@ -646,17 +649,18 @@ cdef class GLDraw:
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
|
||||
clip = (0, 0, self.virtual_size[0], self.virtual_size[1])
|
||||
self.default_clip = (0, 0, self.virtual_size[0], self.virtual_size[1])
|
||||
clip = self.default_clip
|
||||
|
||||
self.upscale_factor = 1.0 * self.physical_size[0] / self.virtual_size[0]
|
||||
|
||||
if renpy.audio.music.get_playing("movie") and renpy.display.video.fullscreen:
|
||||
surf = renpy.display.video.render_movie(self.virtual_size[0], self.virtual_size[1])
|
||||
if surf is not None:
|
||||
self.draw_transformed(surf, clip, 0, 0, 1.0, reverse)
|
||||
self.draw_transformed(surf, clip, 0, 0, 1.0, 1.0, reverse)
|
||||
|
||||
else:
|
||||
self.draw_transformed(surftree, clip, 0, 0, 1.0, reverse)
|
||||
self.draw_transformed(surftree, clip, 0, 0, 1.0, 1.0, reverse)
|
||||
|
||||
if flip:
|
||||
|
||||
@@ -733,6 +737,7 @@ cdef class GLDraw:
|
||||
double xo,
|
||||
double yo,
|
||||
double alpha,
|
||||
double over,
|
||||
render.Matrix2D reverse):
|
||||
|
||||
cdef render.Render rend
|
||||
@@ -751,6 +756,7 @@ cdef class GLDraw:
|
||||
yo,
|
||||
reverse,
|
||||
alpha,
|
||||
over,
|
||||
self.environ,
|
||||
False)
|
||||
|
||||
@@ -759,7 +765,7 @@ cdef class GLDraw:
|
||||
if isinstance(what, pygame.Surface):
|
||||
|
||||
tex = self.load_texture(what)
|
||||
self.draw_transformed(tex, clip, xo, yo, alpha, reverse)
|
||||
self.draw_transformed(tex, clip, xo, yo, alpha, over, reverse)
|
||||
return 0
|
||||
|
||||
raise Exception("Unknown drawing type. " + repr(what))
|
||||
@@ -777,10 +783,10 @@ cdef class GLDraw:
|
||||
# of dissolve on Ren'Py proper.
|
||||
|
||||
self.draw_transformed(rend.children[0][0],
|
||||
clip, xo, yo, alpha, reverse)
|
||||
clip, xo, yo, alpha, over, reverse)
|
||||
|
||||
self.draw_transformed(rend.children[1][0],
|
||||
clip, xo, yo, alpha * what.operation_complete, reverse)
|
||||
clip, xo, yo, alpha * what.operation_complete, over, reverse)
|
||||
|
||||
else:
|
||||
|
||||
@@ -793,6 +799,7 @@ cdef class GLDraw:
|
||||
yo,
|
||||
reverse,
|
||||
alpha,
|
||||
over,
|
||||
rend.operation_complete,
|
||||
self.environ)
|
||||
|
||||
@@ -810,6 +817,7 @@ cdef class GLDraw:
|
||||
yo,
|
||||
reverse,
|
||||
alpha,
|
||||
over,
|
||||
rend.operation_complete,
|
||||
rend.operation_parameter,
|
||||
self.environ)
|
||||
@@ -835,6 +843,7 @@ cdef class GLDraw:
|
||||
yo,
|
||||
reverse,
|
||||
alpha,
|
||||
over,
|
||||
self.environ,
|
||||
True)
|
||||
|
||||
@@ -847,7 +856,7 @@ cdef class GLDraw:
|
||||
# Non-aligned clipping uses RTT.
|
||||
if reverse.ydx != 0 or reverse.xdy != 0:
|
||||
tex = what.render_to_texture(True)
|
||||
self.draw_transformed(tex, clip, xo, yo, alpha, reverse)
|
||||
self.draw_transformed(tex, clip, xo, yo, alpha, over, reverse)
|
||||
return 0
|
||||
|
||||
minx, miny, maxx, maxy = clip
|
||||
@@ -864,7 +873,8 @@ cdef class GLDraw:
|
||||
clip = (minx, miny, maxx, maxy)
|
||||
|
||||
alpha = alpha * rend.alpha
|
||||
|
||||
over = over * rend.over
|
||||
|
||||
# If our alpha has hit 0, don't do anything.
|
||||
if alpha <= 0.003: # (1 / 256)
|
||||
return 0
|
||||
@@ -878,7 +888,7 @@ cdef class GLDraw:
|
||||
tcxo = reverse.xdx * cxo + reverse.xdy * cyo
|
||||
tcyo = reverse.ydx * cxo + reverse.ydy * cyo
|
||||
|
||||
self.draw_transformed(child, clip, xo + tcxo, yo + tcyo, alpha, child_reverse)
|
||||
self.draw_transformed(child, clip, xo + tcxo, yo + tcyo, alpha, over, child_reverse)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -899,9 +909,10 @@ cdef class GLDraw:
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
|
||||
clip = (0, 0, what.width, what.height)
|
||||
self.default_clip = (0, 0, what.width, what.height)
|
||||
clip = self.default_clip
|
||||
|
||||
self.draw_transformed(what, clip, 0, 0, 1.0, reverse)
|
||||
self.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
|
||||
|
||||
if isinstance(what, render.Render):
|
||||
what.is_opaque()
|
||||
@@ -939,7 +950,7 @@ cdef class GLDraw:
|
||||
|
||||
clip = (0, 0, 1, 1)
|
||||
|
||||
self.draw_transformed(what, clip, 0, 0, 1.0, reverse)
|
||||
self.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
|
||||
|
||||
cdef unsigned char pixel[4]
|
||||
|
||||
@@ -978,7 +989,7 @@ cdef class GLDraw:
|
||||
|
||||
clip = (0, 0, width, height)
|
||||
|
||||
draw.draw_transformed(what, clip, 0, 0, 1.0, reverse)
|
||||
draw.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
|
||||
|
||||
if isinstance(what, render.Render):
|
||||
what.is_opaque()
|
||||
@@ -1063,6 +1074,7 @@ cdef class GLDraw:
|
||||
y,
|
||||
IDENTITY,
|
||||
1.0,
|
||||
1.0,
|
||||
self.environ,
|
||||
False)
|
||||
|
||||
|
||||
@@ -121,6 +121,27 @@ varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color0 = texture2D(tex0, TexCoord0.st);
|
||||
gl_FragColor = color0 * Color;
|
||||
}
|
||||
"""
|
||||
|
||||
BLIT_CLIP_SHADER = """
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
uniform vec4 Color;
|
||||
uniform sampler2D tex0;
|
||||
|
||||
varying vec2 TexCoord0;
|
||||
|
||||
varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
|
||||
@@ -149,6 +170,33 @@ varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color0 = texture2D(tex0, TexCoord0.st);
|
||||
vec4 color1 = texture2D(tex1, TexCoord1.st);
|
||||
|
||||
gl_FragColor = mix(color0, color1, done) * Color;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
BLEND_CLIP_SHADER = """
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
uniform vec4 Color;
|
||||
uniform sampler2D tex0;
|
||||
uniform sampler2D tex1;
|
||||
uniform float done;
|
||||
|
||||
varying vec2 TexCoord0;
|
||||
varying vec2 TexCoord1;
|
||||
|
||||
varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
|
||||
@@ -182,6 +230,38 @@ varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color0 = texture2D(tex0, TexCoord0.st);
|
||||
vec4 color1 = texture2D(tex1, TexCoord1.st);
|
||||
vec4 color2 = texture2D(tex2, TexCoord2.st);
|
||||
|
||||
float a = clamp((color0.a + offset) * multiplier, 0.0, 1.0);
|
||||
|
||||
gl_FragColor = mix(color1, color2, a) * Color;
|
||||
}
|
||||
"""
|
||||
|
||||
IMAGEBLEND_CLIP_SHADER = """
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
uniform vec4 Color;
|
||||
uniform sampler2D tex0;
|
||||
uniform sampler2D tex1;
|
||||
uniform sampler2D tex2;
|
||||
uniform float offset;
|
||||
uniform float multiplier;
|
||||
|
||||
varying vec2 TexCoord0;
|
||||
varying vec2 TexCoord1;
|
||||
varying vec2 TexCoord2;
|
||||
|
||||
varying vec2 pos;
|
||||
uniform vec2 clip0;
|
||||
uniform vec2 clip1;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
|
||||
@@ -200,6 +280,7 @@ void main()
|
||||
|
||||
|
||||
|
||||
|
||||
def check_status(shader, handle, type):
|
||||
"""
|
||||
Checks the status of a shader or program. If it fails, then an
|
||||
@@ -351,6 +432,11 @@ cdef class ShaderEnviron(Environ):
|
||||
cdef Program blend_program
|
||||
cdef Program imageblend_program
|
||||
|
||||
cdef Program blit_clip_program
|
||||
cdef Program blend_clip_program
|
||||
cdef Program imageblend_clip_program
|
||||
|
||||
cdef bint clipping
|
||||
cdef double clip_x0
|
||||
cdef double clip_y0
|
||||
cdef double clip_x1
|
||||
@@ -361,11 +447,17 @@ cdef class ShaderEnviron(Environ):
|
||||
cdef int viewport_w
|
||||
cdef int viewport_h
|
||||
|
||||
|
||||
def init(self):
|
||||
|
||||
self.blit_program = Program(VERTEX_SHADER1, BLIT_SHADER)
|
||||
self.blit_clip_program = Program(VERTEX_SHADER1, BLIT_CLIP_SHADER)
|
||||
|
||||
self.blend_program = Program(VERTEX_SHADER2, BLEND_SHADER)
|
||||
self.blend_clip_program = Program(VERTEX_SHADER2, BLEND_CLIP_SHADER)
|
||||
|
||||
self.imageblend_program = Program(VERTEX_SHADER3, IMAGEBLEND_SHADER)
|
||||
self.imageblend_clip_program = Program(VERTEX_SHADER3, IMAGEBLEND_CLIP_SHADER)
|
||||
|
||||
# The current program.
|
||||
self.program = None
|
||||
@@ -392,19 +484,27 @@ cdef class ShaderEnviron(Environ):
|
||||
|
||||
glUseProgramObjectARB(program.program)
|
||||
glUniformMatrix4fvARB(program.Projection, 1, GL_FALSE, self.projection)
|
||||
glUniform2fARB(program.clip0, self.clip_x0, self.clip_y0)
|
||||
glUniform2fARB(program.clip1, self.clip_x1, self.clip_y1)
|
||||
|
||||
if self.clipping:
|
||||
glUniform2fARB(program.clip0, self.clip_x0, self.clip_y0)
|
||||
glUniform2fARB(program.clip1, self.clip_x1, self.clip_y1)
|
||||
|
||||
cdef void blit(self):
|
||||
|
||||
program = self.blit_program
|
||||
if self.clipping:
|
||||
program = self.blit_clip_program
|
||||
else:
|
||||
program = self.blit_program
|
||||
|
||||
if self.program is not program:
|
||||
self.activate(program)
|
||||
glUniform1iARB(program.tex0, 0)
|
||||
|
||||
cdef void blend(self, double fraction):
|
||||
program = self.blend_program
|
||||
if self.clipping:
|
||||
program = self.blend_clip_program
|
||||
else:
|
||||
program = self.blend_program
|
||||
|
||||
if self.program is not program:
|
||||
self.activate(program)
|
||||
@@ -415,7 +515,10 @@ cdef class ShaderEnviron(Environ):
|
||||
|
||||
cdef void imageblend(self, double fraction, int ramp):
|
||||
|
||||
program = self.imageblend_program
|
||||
if self.clipping:
|
||||
program = self.imageblend_clip_program
|
||||
else:
|
||||
program = self.imageblend_program
|
||||
|
||||
if self.program is not program:
|
||||
self.activate(program)
|
||||
@@ -508,10 +611,15 @@ cdef class ShaderEnviron(Environ):
|
||||
cdef int cx, cy, cw, ch
|
||||
cdef int psw, psh
|
||||
|
||||
if clip_box == draw.default_clip:
|
||||
self.unset_clip(draw)
|
||||
return
|
||||
|
||||
minx, miny, maxx, maxy = clip_box
|
||||
psw, psh = draw.physical_size
|
||||
|
||||
# The clipping box.
|
||||
# The clipping box.
|
||||
self.clipping = True
|
||||
self.clip_x0 = minx
|
||||
self.clip_y0 = miny
|
||||
self.clip_x1 = maxx
|
||||
@@ -566,7 +674,8 @@ cdef class ShaderEnviron(Environ):
|
||||
cdef void unset_clip(self, GLDraw draw):
|
||||
|
||||
glDisable(GL_SCISSOR_TEST)
|
||||
|
||||
|
||||
self.clipping = False
|
||||
self.clip_x0 = 0
|
||||
self.clip_y0 = 0
|
||||
self.clip_x1 = 65535
|
||||
|
||||
@@ -42,6 +42,7 @@ cpdef blit(
|
||||
double sy,
|
||||
render.Matrix2D transform,
|
||||
double alpha,
|
||||
double over,
|
||||
Environ environ,
|
||||
bint nearest)
|
||||
|
||||
@@ -52,6 +53,7 @@ cpdef blend(
|
||||
double sy,
|
||||
render.Matrix2D transform,
|
||||
double alpha,
|
||||
double over,
|
||||
double fraction,
|
||||
Environ environ)
|
||||
|
||||
@@ -63,6 +65,7 @@ cpdef imageblend(
|
||||
double sy,
|
||||
render.Matrix2D transform,
|
||||
double alpha,
|
||||
double over,
|
||||
double fraction,
|
||||
int ramp,
|
||||
Environ environ)
|
||||
|
||||
+16
-24
@@ -52,7 +52,7 @@ cdef GLenum rtt_format = GL_RGBA
|
||||
cdef GLenum rtt_internalformat = GL_RGBA
|
||||
cdef GLenum rtt_type = GL_UNSIGNED_BYTE
|
||||
|
||||
def use_angle():
|
||||
def use_gles():
|
||||
global tex_format
|
||||
global tex_internalformat
|
||||
global tex_type
|
||||
@@ -68,22 +68,6 @@ def use_angle():
|
||||
rtt_internalformat = GL_RGBA
|
||||
rtt_type = GL_UNSIGNED_BYTE
|
||||
|
||||
def use_gles():
|
||||
global tex_format
|
||||
global tex_internalformat
|
||||
global tex_type
|
||||
global rtt_format
|
||||
global rtt_internalformat
|
||||
global rtt_type
|
||||
|
||||
tex_format = GL_RGBA
|
||||
tex_internalformat = GL_RGBA
|
||||
tex_type = GL_UNSIGNED_BYTE
|
||||
|
||||
rtt_format = GL_RGB
|
||||
rtt_internalformat = GL_RGB
|
||||
rtt_type = GL_UNSIGNED_BYTE
|
||||
|
||||
def use_gl():
|
||||
global tex_format
|
||||
global tex_internalformat
|
||||
@@ -889,7 +873,7 @@ def align_axes(*args):
|
||||
return rv
|
||||
|
||||
|
||||
cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, double alpha, Environ environ, bint nearest):
|
||||
cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, double alpha, double over, Environ environ, bint nearest):
|
||||
"""
|
||||
This draws texgrid `tg` to the screen. `sx` and `sy` are offsets from
|
||||
the upper-left corner of the screen.
|
||||
@@ -898,6 +882,8 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
|
||||
texgrid coordinates to screen coordinates.
|
||||
|
||||
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
|
||||
|
||||
`over` is the over blending factor.
|
||||
"""
|
||||
|
||||
cdef int x, y
|
||||
@@ -906,7 +892,7 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
|
||||
tg.make_ready(nearest)
|
||||
|
||||
environ.blit()
|
||||
environ.set_color(alpha, alpha, alpha, alpha)
|
||||
environ.set_color(alpha, alpha, alpha, over * alpha)
|
||||
|
||||
y = 0
|
||||
|
||||
@@ -932,7 +918,7 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
|
||||
|
||||
y += texh
|
||||
|
||||
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matrix2D transform, double alpha, double fraction, Environ environ):
|
||||
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, Environ environ):
|
||||
"""
|
||||
Blends two textures to the screen.
|
||||
|
||||
@@ -945,6 +931,8 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
|
||||
|
||||
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
|
||||
|
||||
`over` is the over blending factor.
|
||||
|
||||
`fraction` is the fraction of the second texture to show.
|
||||
"""
|
||||
|
||||
@@ -952,7 +940,7 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
|
||||
tg1.make_ready(False)
|
||||
|
||||
environ.blend(fraction)
|
||||
environ.set_color(alpha, alpha, alpha, alpha)
|
||||
environ.set_color(alpha, alpha, alpha, over * alpha)
|
||||
|
||||
y = 0
|
||||
|
||||
@@ -985,8 +973,9 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
|
||||
x += t0w
|
||||
|
||||
y += t0h
|
||||
|
||||
|
||||
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, render.Matrix2D transform, double alpha, double fraction, int ramp, Environ environ):
|
||||
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, int ramp, Environ environ):
|
||||
"""
|
||||
This uses texture 0 to control the blending of tetures 1 and 2 to
|
||||
the screen.
|
||||
@@ -998,7 +987,10 @@ cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, d
|
||||
`transform` is the transform to apply to the texgrid, when going from
|
||||
texgrid coordinates to screen coordinates.
|
||||
|
||||
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
|
||||
`over` is the over blending factor.
|
||||
|
||||
`additive` is the additive blending factor, which is 1.0 for fully additive,
|
||||
and 0.0 for fully over blending.
|
||||
|
||||
`fraction` is the fraction of the second texture to show.
|
||||
|
||||
@@ -1011,7 +1003,7 @@ cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, d
|
||||
tg2.make_ready(False)
|
||||
|
||||
environ.imageblend(fraction, ramp)
|
||||
environ.set_color(alpha, alpha, alpha, alpha)
|
||||
environ.set_color(alpha, alpha, alpha, over * alpha)
|
||||
|
||||
y = 0
|
||||
|
||||
|
||||
+21
-4
@@ -26,12 +26,29 @@ from cStringIO import StringIO
|
||||
import sys
|
||||
import types
|
||||
|
||||
# Ensure the utf-8 codec is loaded, to prevent recursion when we use it
|
||||
# to look up filenames.
|
||||
u"".encode("utf-8")
|
||||
|
||||
try:
|
||||
import android.apk
|
||||
apks = [
|
||||
android.apk.APK(prefix='assets/x-game/'),
|
||||
android.apk.APK(prefix='assets/x-common/'),
|
||||
]
|
||||
|
||||
expansion = os.environ.get("ANDROID_EXPANSION", None)
|
||||
if expansion is not None:
|
||||
print "Using expansion file", expansion
|
||||
|
||||
apks = [
|
||||
android.apk.APK(apk=expansion, prefix='assets/x-game/'),
|
||||
android.apk.APK(apk=expansion, prefix='assets/x-common/'),
|
||||
]
|
||||
else:
|
||||
print "Not using expansion file."
|
||||
|
||||
apks = [
|
||||
android.apk.APK(prefix='assets/x-game/'),
|
||||
android.apk.APK(prefix='assets/x-common/'),
|
||||
]
|
||||
|
||||
except ImportError:
|
||||
apks = [ ]
|
||||
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ class LogFile(object):
|
||||
return False
|
||||
|
||||
try:
|
||||
base = os.environ.get("RENPY_LOG_BASE", renpy.config.basedir)
|
||||
base = os.environ.get("RENPY_LOG_BASE", renpy.config.logdir)
|
||||
fn = os.path.join(base, self.name + ".txt")
|
||||
|
||||
altfn = os.path.join(tempfile.gettempdir(), "renpy-" + self.name + ".txt")
|
||||
|
||||
+6
-4
@@ -86,6 +86,7 @@ def run(restart):
|
||||
renpy.store._restart = restart
|
||||
|
||||
# We run until we get an exception.
|
||||
renpy.display.interface.enter_context()
|
||||
renpy.execution.run_context(True)
|
||||
|
||||
|
||||
@@ -153,6 +154,10 @@ def main():
|
||||
renpy.config.commondir = commondir
|
||||
else:
|
||||
renpy.config.commondir = None
|
||||
|
||||
if renpy.android:
|
||||
renpy.config.searchpath = [ ]
|
||||
renpy.config.commondir = None
|
||||
|
||||
# Load Ren'Py extensions.
|
||||
for dir in renpy.config.searchpath: #@ReservedAssignment
|
||||
@@ -332,10 +337,7 @@ def main():
|
||||
except game.QuitException, e:
|
||||
|
||||
if e.relaunch:
|
||||
if renpy.windows and sys.argv[0].endswith(".exe"):
|
||||
subprocess.Popen(sys.argv)
|
||||
else:
|
||||
subprocess.Popen([sys.executable, "-OO"] + sys.argv)
|
||||
subprocess.Popen([sys.executable, "-EOO"] + sys.argv)
|
||||
|
||||
break
|
||||
|
||||
|
||||
+7
-3
@@ -1,3 +1,4 @@
|
||||
|
||||
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
@@ -298,6 +299,10 @@ def list_logical_lines(filename, filedata=None):
|
||||
word = prefix + rest
|
||||
|
||||
line += word
|
||||
|
||||
if len(line) > 65536:
|
||||
raise ParseError(filename, start_number, "Overly long logical line. (Check strings and parenthesis.)", line=line, first=True)
|
||||
|
||||
pos = m.end(0)
|
||||
|
||||
# print repr(data[pos:])
|
||||
@@ -938,9 +943,6 @@ class Lexer(object):
|
||||
|
||||
for _fn, ln, text, subblock in block:
|
||||
|
||||
if o.line > ln:
|
||||
assert False
|
||||
|
||||
while o.line < ln:
|
||||
rv.append(indent + '\n')
|
||||
o.line += 1
|
||||
@@ -1267,6 +1269,7 @@ def parse_parameters(l):
|
||||
names.add(name)
|
||||
|
||||
if l.match(r'='):
|
||||
l.skip_whitespace()
|
||||
default = l.delimited_python("),")
|
||||
else:
|
||||
default = None
|
||||
@@ -1324,6 +1327,7 @@ def parse_arguments(l):
|
||||
l.revert(state)
|
||||
name = None
|
||||
|
||||
l.skip_whitespace()
|
||||
arguments.append((name, l.delimited_python("),")))
|
||||
|
||||
if l.match(r'\)'):
|
||||
|
||||
+43
-4
@@ -479,13 +479,21 @@ def mutator(method):
|
||||
if id(self) not in mutated:
|
||||
mutated[id(self)] = ( weakref.ref(self), self.get_rollback())
|
||||
mutate_flag = True
|
||||
|
||||
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return do_mutation
|
||||
|
||||
class RevertableList(list):
|
||||
|
||||
def __init__(self, *args):
|
||||
log = renpy.game.log
|
||||
|
||||
if log is not None:
|
||||
log.mutated[id(self)] = None
|
||||
|
||||
list.__init__(self, *args)
|
||||
|
||||
__delitem__ = mutator(list.__delitem__)
|
||||
__delslice__ = mutator(list.__delslice__)
|
||||
__setitem__ = mutator(list.__setitem__)
|
||||
@@ -526,6 +534,14 @@ def revertable_sorted(*args, **kwargs):
|
||||
|
||||
class RevertableDict(dict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
log = renpy.game.log
|
||||
|
||||
if log is not None:
|
||||
log.mutated[id(self)] = None
|
||||
|
||||
dict.__init__(self, *args, **kwargs)
|
||||
|
||||
__delitem__ = mutator(dict.__delitem__)
|
||||
__setitem__ = mutator(dict.__setitem__)
|
||||
clear = mutator(dict.clear)
|
||||
@@ -561,6 +577,14 @@ class RevertableDict(dict):
|
||||
|
||||
class RevertableSet(sets.Set):
|
||||
|
||||
def __init__(self, *args):
|
||||
log = renpy.game.log
|
||||
|
||||
if log is not None:
|
||||
log.mutated[id(self)] = None
|
||||
|
||||
sets.Set.__init__(self, *args)
|
||||
|
||||
__iand__ = mutator(sets.Set.__iand__)
|
||||
__ior__ = mutator(sets.Set.__ior__)
|
||||
__isub__ = mutator(sets.Set.__isub__)
|
||||
@@ -610,6 +634,15 @@ class RevertableSet(sets.Set):
|
||||
|
||||
class RevertableObject(object):
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
self = super(RevertableObject, cls).__new__(cls, *args, **kwargs)
|
||||
|
||||
log = renpy.game.log
|
||||
if log is not None:
|
||||
log.mutated[id(self)] = None
|
||||
|
||||
return self
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
object.__setattr__(self, attr, value)
|
||||
|
||||
@@ -783,7 +816,8 @@ class Rollback(renpy.object.Object):
|
||||
"""
|
||||
|
||||
for obj, roll in reversed(self.objects):
|
||||
obj.rollback(roll)
|
||||
if roll is not None:
|
||||
obj.rollback(roll)
|
||||
|
||||
for name, changes in self.stores.iteritems():
|
||||
store = store_dicts.get(name, None)
|
||||
@@ -925,7 +959,12 @@ class RollbackLog(renpy.object.Object):
|
||||
self.current.objects = [ ]
|
||||
|
||||
try:
|
||||
for _k, (ref, roll) in self.mutated.iteritems():
|
||||
for _k, v in self.mutated.iteritems():
|
||||
|
||||
if v is None:
|
||||
continue
|
||||
|
||||
(ref, roll) = v
|
||||
|
||||
obj = ref()
|
||||
if obj is None:
|
||||
@@ -1239,7 +1278,7 @@ class RollbackLog(renpy.object.Object):
|
||||
store[name] = value
|
||||
|
||||
# Now, rollback to an acceptable point.
|
||||
self.rollback(0, force=True, label=label)
|
||||
self.rollback(0, force=True, label=label, greedy=False)
|
||||
|
||||
# Because of the rollback, we never make it this far.
|
||||
|
||||
|
||||
+17
-3
@@ -107,9 +107,23 @@ def expand_anchor(v):
|
||||
"""
|
||||
Turns an anchor into a number.
|
||||
"""
|
||||
|
||||
return anchors.get(v, v)
|
||||
|
||||
|
||||
try:
|
||||
return anchors.get(v, v)
|
||||
except:
|
||||
# This fixes some bugs in very old Ren'Pys.
|
||||
|
||||
for n in anchors:
|
||||
o = getattr(renpy.store, n, None)
|
||||
if o is None:
|
||||
continue
|
||||
|
||||
if v is o:
|
||||
return anchors[n]
|
||||
|
||||
raise
|
||||
|
||||
|
||||
|
||||
# A map of properties that we know about. The properties may take a
|
||||
# function that is called to convert the argument to something more
|
||||
|
||||
+18
-1
@@ -145,6 +145,11 @@ class SFont(ImageFont):
|
||||
self.advance[u' '] = self.spacewidth
|
||||
self.offsets[u' '] = (0, 0)
|
||||
|
||||
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
|
||||
self.width[u'\u200b'] = 0
|
||||
self.advance[u'\u200b'] = 0
|
||||
self.offsets[u'\u200b'] = (0, 0)
|
||||
|
||||
self.chars[u'\u00a0'] = self.chars[u' ']
|
||||
self.width[u'\u00a0'] = self.width[u' ']
|
||||
self.advance[u'\u00a0'] = self.advance[u' ']
|
||||
@@ -252,13 +257,19 @@ class MudgeFont(ImageFont):
|
||||
self.advance[u' '] = self.spacewidth
|
||||
self.offsets[u' '] = (0, 0)
|
||||
|
||||
|
||||
if u'\u00a0' not in self.chars:
|
||||
self.chars[u'\u00a0'] = self.chars[u' ']
|
||||
self.width[u'\u00a0'] = self.width[u' ']
|
||||
self.advance[u'\u00a0'] = self.advance[u' ']
|
||||
self.offsets[u'\u00a0'] = self.offsets[u' ']
|
||||
|
||||
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
|
||||
self.width[u'\u200b'] = 0
|
||||
self.advance[u'\u200b'] = 0
|
||||
self.offsets[u'\u200b'] = (0, 0)
|
||||
|
||||
|
||||
|
||||
def parse_bmfont_line(l):
|
||||
w = ""
|
||||
line = [ ]
|
||||
@@ -342,6 +353,12 @@ class BMFont(ImageFont):
|
||||
self.advance[u'\u00a0'] = self.advance[u' ']
|
||||
self.offsets[u'\u00a0'] = self.offsets[u' ']
|
||||
|
||||
|
||||
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, self.height), True)
|
||||
self.width[u'\u200b'] = 0
|
||||
self.advance[u'\u200b'] = 0
|
||||
self.offsets[u'\u200b'] = (0, 0)
|
||||
|
||||
|
||||
def register_sfont(name=None, size=None, bold=False, italics=False, underline=False,
|
||||
filename=None, spacewidth=10, default_kern=0, kerns={},
|
||||
|
||||
@@ -314,7 +314,7 @@ cdef class FTFont:
|
||||
|
||||
rv.index = index
|
||||
|
||||
error = FT_Load_Glyph(face, index, 0)
|
||||
error = FT_Load_Glyph(face, index, FT_LOAD_FORCE_AUTOHINT)
|
||||
if error:
|
||||
raise FreetypeError(error)
|
||||
|
||||
@@ -525,6 +525,9 @@ cdef class FTFont:
|
||||
if glyph.split == SPLIT_INSTEAD:
|
||||
continue
|
||||
|
||||
if glyph.character == 0x200b:
|
||||
continue
|
||||
|
||||
x = glyph.x + xo
|
||||
y = glyph.y + yo
|
||||
|
||||
|
||||
+11
-3
@@ -402,7 +402,7 @@ class Layout(object):
|
||||
`width`, `height`
|
||||
The height of the laid-out text.
|
||||
"""
|
||||
|
||||
|
||||
style = text.style
|
||||
|
||||
self.line_overlap_split = style.line_overlap_split
|
||||
@@ -1020,6 +1020,10 @@ class Layout(object):
|
||||
|
||||
return 0
|
||||
|
||||
# The maximum number of entries in the layout cache.
|
||||
LAYOUT_CACHE_SIZE = 50
|
||||
|
||||
# Maps from a text to the layout of that text - in an old and new generation.
|
||||
layout_cache_old = { }
|
||||
layout_cache_new = { }
|
||||
|
||||
@@ -1087,9 +1091,9 @@ class Text(renpy.display.core.Displayable):
|
||||
self.start = None
|
||||
self.end = None
|
||||
self.dirty = True
|
||||
|
||||
|
||||
def __init__(self, text, slow=None, scope=None, substitute=None, slow_done=None, replaces=None, **properties):
|
||||
|
||||
|
||||
super(Text, self).__init__(**properties)
|
||||
|
||||
# We need text to be a list, so if it's not, wrap it.
|
||||
@@ -1357,6 +1361,10 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
if layout is None or layout.width != width or layout.height != height:
|
||||
layout = Layout(self, width, height, renders)
|
||||
|
||||
if len(layout_cache_new) > LAYOUT_CACHE_SIZE:
|
||||
layout_cache_new.clear()
|
||||
|
||||
layout_cache_new[id(self)] = layout
|
||||
|
||||
# The laid-out size of this Text.
|
||||
|
||||
@@ -315,7 +315,7 @@ class StringTranslator(object):
|
||||
new = self.translations.get(notags, None)
|
||||
|
||||
if new is not None:
|
||||
return True
|
||||
return new
|
||||
|
||||
return old
|
||||
|
||||
|
||||
@@ -25,5 +25,5 @@ fi
|
||||
if [ "$1" = "--build" ] ; then
|
||||
echo "Ren'Py build complete."
|
||||
else
|
||||
exec ./renpy.py "$@"
|
||||
exec python -OO ./renpy.py "$@"
|
||||
fi
|
||||
|
||||
@@ -447,3 +447,29 @@ And install the packages. Then, try building your game again.
|
||||
|
||||
http://lemmasoft.renai.us/forums/viewtopic.php?f=32&t=13987&hilit=rapt
|
||||
|
||||
Expansion APKs
|
||||
--------------
|
||||
|
||||
|PGS4A| optionally supports the use of expansion APKs when used on a
|
||||
device supporting Google Play. Please see:
|
||||
|
||||
http://developer.android.com/google/play/expansion-files.html
|
||||
|
||||
For information about expansion APKs work. Right now, only the
|
||||
main expansion APK is supported, giving a 2GB limit.
|
||||
|
||||
.. ifconfig:: not is_renpy
|
||||
|
||||
When an android APK is created, all assets (from the assets
|
||||
directory) are placed in the expansion APK. The assets can be
|
||||
accessed through the :module:`android.assets` interface.
|
||||
|
||||
.. ifconfig:: is_renpy
|
||||
|
||||
When an APK is created, all game files will be placed in the
|
||||
expansion APK. Ren'Py will transparently use these files.
|
||||
|
||||
|PGS4A| will place the expansion APK on the device when installing
|
||||
the APK package on the device. In normal operation, Google Play will
|
||||
place the expansion APK on the device automatically.
|
||||
|
||||
|
||||
@@ -45,20 +45,8 @@ due to the Android software and hardware are:
|
||||
screen. When the user is not touching the screen, the virtual
|
||||
pointer will move to the upper-left corner of the screen.
|
||||
|
||||
* :func:`ImageDissolve`, :func:`AlphaDissolve`, and :func:`AlphaBlend`
|
||||
are not supported.
|
||||
|
||||
* Functions that render to a texture can only render an opaque
|
||||
texture. This means that the :func:`Dissolve` and :func:`Pixellate`
|
||||
transitions will only produce opaque output.
|
||||
|
||||
* The :propref:`focus_mask` property is not supported. It will be
|
||||
treated as if all pixels in the mask are opaque.
|
||||
|
||||
* Movie playback is not supported.
|
||||
|
||||
* Launching the web browser is not supported.
|
||||
|
||||
* Some python modules (including network communication) modules are
|
||||
not supported.
|
||||
|
||||
|
||||
@@ -751,6 +751,37 @@ both horizontal and vertical positions.
|
||||
|
||||
This controls the opacity of the displayable.
|
||||
|
||||
The alpha transform is applied to each image comprising the child of
|
||||
the transform independently. This can lead to unexpected results when
|
||||
the children overlap, such as as seeing a character through clothing.
|
||||
The :func:`Flatten` displayable can help with these problems.
|
||||
|
||||
.. transform-property:: additive
|
||||
|
||||
:type: float
|
||||
:default: 0.0
|
||||
|
||||
This controls how much additive blending Ren'Py performs. When 1.0,
|
||||
Ren'Py draws using the ADD operator. When 0.0, Ren'Py draws using
|
||||
the OVER operator.
|
||||
|
||||
Additive blending is performed on each child of the transform independently.
|
||||
|
||||
Fully additive blending doesn't alter the alpha channel of the destination,
|
||||
and additive images may not be visible if they're not drawn directly onto
|
||||
an opaque surface. (Complex operations, like viewport, :func:`Flatten`, :func:`Frame`,
|
||||
and certain transitions may cause problems with additive blending.)
|
||||
|
||||
.. warning::
|
||||
|
||||
Additive blending is only supported by hardware-based renderers, such
|
||||
as the OpenGL and DirectX/ANGLE renderers. The software renderer will
|
||||
draw additive images incorrectly.
|
||||
|
||||
Once the graphics system has started, ``renpy.get_renderer_info()["additive"]``
|
||||
will be true if additive blending is supported.
|
||||
|
||||
|
||||
.. transform-property:: around
|
||||
|
||||
:type: (position, position)
|
||||
|
||||
@@ -209,8 +209,19 @@ Please think twice about archiving your game. Keeping files open will
|
||||
help others run your game on future platforms - platforms that may not
|
||||
exist until after you're gone.
|
||||
|
||||
|
||||
Build Functions
|
||||
---------------
|
||||
|
||||
.. include:: inc/build
|
||||
|
||||
Advanced Configuration
|
||||
----------------------
|
||||
|
||||
The following variables provide further control of the build process:
|
||||
|
||||
.. var:: build.exclude_empty_directories = True
|
||||
|
||||
If true, empty directories (including directories left empty by
|
||||
file archiving) will be removed from generated packages. If false,
|
||||
empty directories will be included.
|
||||
|
||||
+129
-51
@@ -2,6 +2,85 @@
|
||||
Full Changelog
|
||||
==============
|
||||
|
||||
Ren'Py 6.15.6
|
||||
-------------
|
||||
|
||||
This release includes improvements for the Android platform:
|
||||
|
||||
* Assets are now read exclusively from the APK and expansion file.
|
||||
* Logs and tracebacks are placed on external storage.
|
||||
* Saves are placed on external storage, except when saves from
|
||||
older versions of Ren'Py exist.
|
||||
|
||||
The GL2 shaders Ren'Py uses have been simplified in the (usual) case
|
||||
where no clipping is occuring. This leads to a noticable speed
|
||||
improvement on Android, and potentially other platforms as well.
|
||||
|
||||
An issue with Drag-and-drop has been fixed. Thanks go to Kinsman
|
||||
for contributing this fixe.
|
||||
|
||||
The :func:`Skip` action now triggers the skip indicator. It also
|
||||
supports a new fast parameter, which causes skipping to the
|
||||
next menu.
|
||||
|
||||
This release includes various minor changes to improve compatibility
|
||||
with very old Ren'Py games. (It now runs the Ren'Py 5 demo.)
|
||||
|
||||
|
||||
Ren'Py 6.15.5
|
||||
-------------
|
||||
|
||||
This release adds two new features:
|
||||
|
||||
* The GL renderer now supports additive blending. This is enabled using the
|
||||
:tpref:`additive` transform property in an ATL transform or use of the
|
||||
:func:`Transform` class. Additive blending will not work if the software
|
||||
renderer is in use, and it's up to creators to deal with that issue.
|
||||
|
||||
* The new :func:`Flatten` displayable combines multiple textures into
|
||||
a single texture. This can be used to prevent incorrect behavior
|
||||
when a displayable containing multiple overlapping textures (like a
|
||||
:func:`LiveComposite` is shown with an :tpref:`alpha` between 0 and 1.
|
||||
|
||||
It also fixes the following issues:
|
||||
|
||||
* Whitespace is now skipped before default arguments, which previously
|
||||
caused parse errors in some cases.
|
||||
|
||||
* Ren'Py now sets the unix mode of files and directories in zip and tar
|
||||
files to 644 and 755 as appropriate. Prior versions of Ren'Py used
|
||||
666 and 777 as the permissions, which lead to a security problem
|
||||
when the file was unpacked by a tool that didn't respect the user's
|
||||
umask. (Info-zip had this problem.)
|
||||
|
||||
* Auto-hinting for fonts is now enabled by default. This restores font
|
||||
rendering compatibility with prior releases.
|
||||
|
||||
* Ren'Py now builds with and requires the current version of libav. It
|
||||
should also work with current versions of ffmpeg when libav is
|
||||
available.
|
||||
|
||||
* The version of SDL distributed with Ren'Py has been patched to
|
||||
prevent multiple windows from showing up in the Window menu
|
||||
when entering and leaving fullscreen mode.
|
||||
|
||||
|
||||
|
||||
Ren'Py 6.15.4
|
||||
-------------
|
||||
|
||||
This release fixes a compile problem that prevented Ren'Py 6.14.x and Ren'Py
|
||||
6.15.0-3 from running on most 64-bit Linux systems.
|
||||
|
||||
Image prediction has become more fine-grained, and can take place while the
|
||||
screen is animating.
|
||||
|
||||
The new :var:`build.exclude_empty_directories` determines if empty directories
|
||||
are include or excluded from the distribution. It defaults to true,
|
||||
previously the default was platform-dependant.
|
||||
|
||||
|
||||
|
||||
Ren'Py 6.15
|
||||
===========
|
||||
|
||||
@@ -18,21 +97,20 @@ combined at the translator's discretion. As most Ren'Py statements are
|
||||
allowed inside the new translation blocks, it's possible to use logic (like
|
||||
conditions) to tailor the translations to your language.
|
||||
|
||||
The launcher includes a new "Generate Translations" button, which - as part
|
||||
of a sanctioned translation where the full script is present - will generate
|
||||
The launcher includes a new "Generate Translations" button, which - as part of
|
||||
a sanctioned translation where the full script is present - will generate
|
||||
empty translation files for a new language.
|
||||
|
||||
Improved Japanese Support
|
||||
-------------------------
|
||||
|
||||
Ren'Py 6.15 includes multiple changes to better support the Japanese
|
||||
language.
|
||||
Ren'Py 6.15 includes multiple changes to better support the Japanese language.
|
||||
|
||||
* The tutorial game has been translated to Japanese, with the language
|
||||
being selectable from the preferences menu.
|
||||
* The tutorial game has been translated to Japanese, with the language being
|
||||
selectable from the preferences menu.
|
||||
|
||||
The tutorial was translated by Koichi Akabe.
|
||||
|
||||
|
||||
* Support for vertical writing has been added to Ren'Py. Consisting of the
|
||||
:propref:`vertical` style property for text, and the new
|
||||
:propref:`box_reverse` property on hboxes, this support makes it possible
|
||||
@@ -96,14 +174,13 @@ replay if one is in progress, and is ignored otherwise.
|
||||
Voice Improvements
|
||||
------------------
|
||||
|
||||
There have been several improvements to the voice playback system. The
|
||||
new :var:`config.voice_filename_format` variable makes it possible to
|
||||
use only part of the filename in a voice statement. The new voice_tag
|
||||
parameter to :func:`Character`, in conjunction with the
|
||||
:func:`SetVoiceMute` and :func:`ToggleVoiceMute` actions, makes it
|
||||
possible to selectively mute particular characters' voices. The new
|
||||
:func:`VoiceReplay` action makes it possible to replay the current
|
||||
voice.
|
||||
There have been several improvements to the voice playback system. The new
|
||||
:var:`config.voice_filename_format` variable makes it possible to use only
|
||||
part of the filename in a voice statement. The new voice_tag parameter to
|
||||
:func:`Character`, in conjunction with the :func:`SetVoiceMute` and
|
||||
:func:`ToggleVoiceMute` actions, makes it possible to selectively mute
|
||||
particular characters' voices. The new :func:`VoiceReplay` action makes it
|
||||
possible to replay the current voice.
|
||||
|
||||
Launcher Improvements
|
||||
---------------------
|
||||
@@ -124,44 +201,44 @@ There were a few launcher improvements in this release.
|
||||
Macintosh Changes
|
||||
-----------------
|
||||
|
||||
The Macintosh version of Ren'Py now requires a 64-bit capable processor,
|
||||
and Mac OS X 10.6 or newer.
|
||||
|
||||
The Macintosh version of Ren'Py now requires a 64-bit capable processor, and
|
||||
Mac OS X 10.6 or newer.
|
||||
|
||||
Packaging Improvements
|
||||
----------------------
|
||||
|
||||
The file layout of Ren'Py games has been somewhat altered. With the
|
||||
exception of small launcher programs, all platform-dependent binaries
|
||||
are under the lib/ directory. Ren'Py itself has now been placed in the
|
||||
renpy/ directory. The common/ directory has been moved to
|
||||
renpy/common/, as it's considered an integral part of Ren'Py.
|
||||
The file layout of Ren'Py games has been somewhat altered. With the exception
|
||||
of small launcher programs, all platform-dependent binaries are under the
|
||||
lib/ directory. Ren'Py itself has now been placed in the renpy/ directory.
|
||||
The common/ directory has been moved to renpy/common/, as it's considered an
|
||||
integral part of Ren'Py.
|
||||
|
||||
Ren'Py now uses renamed but otherwise unmodified python binaries on
|
||||
all desktop platforms. (Previously, it used platform-specific
|
||||
binaries.) Portions of the library are shared between the desktop
|
||||
builds.
|
||||
Ren'Py now uses renamed but otherwise unmodified python binaries on all
|
||||
desktop platforms. (Previously, it used platform-specific binaries.) Portions
|
||||
of the library are shared between the desktop builds.
|
||||
|
||||
A running Ren'Py process on Linux will now be named after the game, rather
|
||||
than having python as a name.
|
||||
|
||||
A running Ren'Py process on Linux will now be named after the game,
|
||||
rather than having python as a name.
|
||||
|
||||
|
||||
|
||||
|
||||
Other Changes
|
||||
-------------
|
||||
|
||||
* :ref:`Viewports <sl-viewport>` now support edge scrolling, which
|
||||
scrolls the viewport when the mouse is within a a configurable
|
||||
distance of the viewport edge.
|
||||
* :ref:`Viewports <sl-viewport>` now support edge scrolling, which scrolls
|
||||
the viewport when the mouse is within a a configurable distance of the
|
||||
viewport edge.
|
||||
|
||||
* Most keyboard keys now automatically repeat. The repeat rate is
|
||||
controlled by :var:`config.key_repeat`.
|
||||
* Most keyboard keys now automatically repeat. The repeat rate is controlled
|
||||
by :var:`config.key_repeat`.
|
||||
|
||||
* Side images can now be used with menus.
|
||||
|
||||
* The :ref:`config.enter_yesno_transition` and `config.exit_yesno_transition`
|
||||
variables make it possible to define a transition that is run when
|
||||
yes/no prompts appear and disappear, respectively.
|
||||
|
||||
* The :ref:`config.enter_yesno_transition` and
|
||||
`config.exit_yesno_transition` variables make it possible to define a
|
||||
transition that is run when yes/no prompts appear and disappear,
|
||||
respectively.
|
||||
|
||||
* The :ref:`viewport statement <sl-viewport>` now supports edge scrolling -
|
||||
automatic scrolling when the mouse approaches the sides of the viewport.
|
||||
@@ -216,20 +293,21 @@ Among others, the following bugs were fixed:
|
||||
|
||||
* :ghbug:`51`: The slow_done callback was not called after a rollback.
|
||||
|
||||
* :ghbug:`56`, :ghbug:`57`: :func:`renpy.loadable` now works with Android assets.
|
||||
* :ghbug:`56`, :ghbug:`57`: :func:`renpy.loadable` now works with Android
|
||||
assets.
|
||||
|
||||
* :ghbug:`60`: Fixed a bug that prevented {p} and {w} from working properly when followed
|
||||
immediately by a text tag.
|
||||
|
||||
* :ghbug:`61`: Ren'Py no longer crashes when an end_game_transition is
|
||||
set and a screen uses a variable that is no longer defined when the
|
||||
game restarts.
|
||||
* :ghbug:`60`: Fixed a bug that prevented {p} and {w} from working properly
|
||||
when followed immediately by a text tag.
|
||||
|
||||
* :ghbug:`65`: Multiplying a rollback list by a number now always produces
|
||||
a rollback list.
|
||||
* :ghbug:`61`: Ren'Py no longer crashes when an end_game_transition is set
|
||||
and a screen uses a variable that is no longer defined when the game
|
||||
restarts.
|
||||
|
||||
* :ghbug:`65`: Multiplying a rollback list by a number now always produces a
|
||||
rollback list.
|
||||
|
||||
* Editra should work better on Windows.
|
||||
|
||||
|
||||
* It's now possible to :func:`renpy.call` a label that doesn't take
|
||||
parameters.
|
||||
|
||||
@@ -239,7 +317,7 @@ Among others, the following bugs were fixed:
|
||||
* Fixed an error handling failure when a python early block contained a
|
||||
syntax error.
|
||||
|
||||
|
||||
|
||||
|
||||
Ren'Py 6.14
|
||||
===========
|
||||
@@ -463,7 +541,7 @@ Other Changes
|
||||
|
||||
* The :func:`renpy.call` function allows - with major and important caveats
|
||||
- a call to a Ren'Py label to begin from inside python code. Such a call
|
||||
immediately terminates the current statement.
|
||||
immediately terminates the current statement.
|
||||
|
||||
* When an action is expected, nested lists of actions can be given. The
|
||||
lists are flattened and the action executed.
|
||||
|
||||
@@ -934,6 +934,18 @@ Rarely or Internally Used
|
||||
wav files are of a lower rate, changing this to that rate may make
|
||||
things more efficent.
|
||||
|
||||
.. var:: config.start_callbacks = [ ... ]
|
||||
|
||||
A list of callbacks functions that are called with no arguments
|
||||
after the init phase, but before the game (including the
|
||||
splashscreen) starts. This is intended to be used by frameworks
|
||||
to initialize variables that will be saved.
|
||||
|
||||
The default value of this variable includes callbacks that Ren'Py
|
||||
uses internally to implement features such as nvl-mode. New
|
||||
callbacks can be appended to this list, but the existing callbacks
|
||||
should not be removed.
|
||||
|
||||
.. var:: config.start_interact_callbacks = ...
|
||||
|
||||
A list of functions that are called (without any arguments) when
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Dealing With Display Problems
|
||||
=============================
|
||||
-----------------------------
|
||||
|
||||
As of version 6.13, Ren'Py will take advantage of graphics
|
||||
acceleration hardware, if it's present and functional. Using hardware
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
.. Automatically generated file - do not modify.
|
||||
|
||||
.. function:: Flatten(child, **properties)
|
||||
|
||||
This flattens `child`, which may be made up of multiple textures, into
|
||||
a single texture.
|
||||
|
||||
Certain operations, like the alpha transform property, apply to every
|
||||
texture making up a displayable, which can yield incorrect results
|
||||
when the textures overlap on screen. Flatten creates a single texture
|
||||
from multiple textures, which can prevent this problem.
|
||||
|
||||
Flatten is a relatively expensive operation, and so should only be used
|
||||
when absolutely required.
|
||||
|
||||
.. function:: Frame(image, xborder, yborder, tile=False, **properties)
|
||||
|
||||
A displayable that resizes an image to fill the available area,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.. Automatically generated file - do not modify.
|
||||
|
||||
.. function:: renpy.showing(name, layer='master')
|
||||
|
||||
Returns true if an image with the same tag as `name` is showing on
|
||||
`layer`
|
||||
|
||||
`image`
|
||||
May be a string giving the image name or a tuple giving each
|
||||
component of the image name. It may also be a string giving
|
||||
only the image tag.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
.. Automatically generated file - do not modify.
|
||||
|
||||
.. function:: renpy.input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None)
|
||||
|
||||
Calling this function pops up a window asking the player to enter some
|
||||
text. It returns the entered text.
|
||||
|
||||
`prompt`
|
||||
A string giving a prompt to display to the player.
|
||||
|
||||
`default`
|
||||
A string giving the initial text that will be edited by the player.
|
||||
|
||||
`allow`
|
||||
If not None, a string giving a list of characters that will
|
||||
be allowed in the text.
|
||||
|
||||
`exclude`
|
||||
If not None, if a character is present in this string, it is not
|
||||
allowed in the text.
|
||||
|
||||
`length`
|
||||
If not None, this must be an integer giving the maximum length
|
||||
of the input string.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.. Automatically generated file - do not modify.
|
||||
|
||||
.. class:: MusicRoom(channel='music', fadeout=0.0, fadein=0.0)
|
||||
.. class:: MusicRoom(channel='music', fadeout=0.0, fadein=0.0, loop=False)
|
||||
|
||||
A music room that contains a series of songs that can be unlocked
|
||||
by the user, and actions that can play entries from the list in
|
||||
@@ -16,6 +16,10 @@
|
||||
`fadein`
|
||||
The number of seconds it takes to fade in the new
|
||||
music when changing tracks.
|
||||
|
||||
`loop`
|
||||
If true, a music track will loop once played. If False, it
|
||||
will advance to the next track.
|
||||
|
||||
.. method:: Next(self)
|
||||
|
||||
|
||||
@@ -63,9 +63,12 @@
|
||||
One of ``"gl"`` or ``"sw"``, corresponding to the OpenGL and
|
||||
software renderers, respectively.
|
||||
|
||||
``"resizable``
|
||||
``"resizable"``
|
||||
True if and only if the window is resizable.
|
||||
|
||||
``"additive"``
|
||||
True if and only if the renderer supports additive blending.
|
||||
|
||||
Other, renderer-specific, keys may also exist. The dictionary should
|
||||
be treated as immutable. This should only be called once the display
|
||||
has been started (that is, after the init code is finished).
|
||||
@@ -146,8 +149,8 @@
|
||||
|
||||
.. function:: renpy.set_physical_size(size)
|
||||
|
||||
Attempts to set the size of the physical window to size. This has the
|
||||
side effect of taking the screen out of windowed mode.
|
||||
Attempts to set the size of the physical window to `size`. This has the
|
||||
side effect of taking the screen out of fullscreen mode.
|
||||
|
||||
.. function:: renpy.vibrate(duration)
|
||||
|
||||
|
||||
@@ -55,11 +55,14 @@
|
||||
textbutton "Marsopolis":
|
||||
action [ Jump("mars"), SelectedIf(mars_flag) ]
|
||||
|
||||
.. function:: Skip()
|
||||
.. function:: Skip(fast=False)
|
||||
|
||||
Causes the game to begin skipping. If the game is in a menu
|
||||
context, then this returns to the game. Otherwise, it just
|
||||
enables skipping.
|
||||
|
||||
`fast`
|
||||
If True, skips directly to the next menu choice.
|
||||
|
||||
.. function:: With(transition)
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.. Automatically generated file - do not modify.
|
||||
|
||||
.. function:: build._(s)
|
||||
|
||||
Flags a string as translatable, and returns it immediately. The string
|
||||
will be translated when text displays it.
|
||||
|
||||
.. function:: updater._(s)
|
||||
|
||||
Flags a string as translatable, and returns it immediately. The string
|
||||
will be translated when text displays it.
|
||||
|
||||
@@ -65,6 +65,27 @@
|
||||
`range`
|
||||
The range of the value.
|
||||
|
||||
.. function:: VariableValue(variable, range, max_is_zero=False, style='bar', offset=0, step=None)
|
||||
|
||||
`variable`
|
||||
A string giving the name of the variable to adjust.
|
||||
`range`
|
||||
The range to adjust over.
|
||||
`max_is_zero`
|
||||
If True, then when the field is zero, the value of the
|
||||
bar will be range, and all other values will be shifted
|
||||
down by 1. This works both ways - when the bar is set to
|
||||
the maximum, the field is set to 0.
|
||||
|
||||
This is used internally, for some preferences.
|
||||
`style`
|
||||
The styles of the bar created.
|
||||
`offset`
|
||||
An offset to add to the value.
|
||||
`step`
|
||||
The amount to change the bar by. If None, defaults to 1/10th of
|
||||
the bar.
|
||||
|
||||
.. function:: XScrollValue(viewport)
|
||||
|
||||
The value of an adjustment that horizontally scrolls the the viewport with the
|
||||
|
||||
+10
-1
@@ -44,6 +44,15 @@ Text, Displayables, Transforms, and Transitions
|
||||
transforms
|
||||
transitions
|
||||
atl
|
||||
|
||||
Other Functions
|
||||
---------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
input
|
||||
|
||||
|
||||
Customizing Ren'Py
|
||||
------------------
|
||||
@@ -99,7 +108,7 @@ End-User Documentation
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
display_problems
|
||||
problems
|
||||
|
||||
|
||||
Engine Developer Documentation
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
==========
|
||||
Text Input
|
||||
==========
|
||||
|
||||
With some limitations, Ren'Py can prompt the user to input a small
|
||||
amount of text. This prompting is done by the renpy.input function,
|
||||
which returns the entered text, allowing it to be saved in a variable
|
||||
or otherwise processed.
|
||||
|
||||
Right now, text input is limited to languages that do not require
|
||||
input method (IME) support. Most western languages should work, but
|
||||
Chinese, Japanese, and Korean probably won't.
|
||||
|
||||
The renpy.input function is defined as:
|
||||
|
||||
.. include:: inc/input
|
||||
|
||||
Code that uses renpy.input will often want to process the result
|
||||
further, using standard python string manipulation functions. For
|
||||
example, the following code will ask the player for his or her
|
||||
name and remove leading or trailing whitespace. If the name is
|
||||
empty, it will be replaced by a default name. Finally, it is
|
||||
displayed to the user. ::
|
||||
|
||||
define pov = Character("[povname]")
|
||||
|
||||
python:
|
||||
povname = renpy.input("What is your name?")
|
||||
povname = povname.strip()
|
||||
|
||||
if not povname:
|
||||
povname = "Pat Smith"
|
||||
|
||||
pov "My name is [povname]!"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -143,9 +143,6 @@ Ren'Py statements are made of a few basic parts.
|
||||
letters.
|
||||
|
||||
:dfn:`Image Name`
|
||||
An image name consists of one or more names, separated by
|
||||
spaces. The name ends at the end of the statement, or when a
|
||||
keyword is encountered.
|
||||
|
||||
An :dfn:`image name` consists of one or more names, separated by
|
||||
spaces. The first component of the image name is called the
|
||||
@@ -216,6 +213,8 @@ If the statement takes a block, the line ends with a colon
|
||||
(:). Otherwise, the line just ends.
|
||||
|
||||
|
||||
.. _python-basics:
|
||||
|
||||
Python Expression Syntax
|
||||
========================
|
||||
|
||||
@@ -347,9 +346,11 @@ that the keyword arguments are described in the documentation.
|
||||
Python is a lot more powerful than we have space for in this manual.
|
||||
To learn Python in more detail, we recommend starting with the Python
|
||||
tutorial, which is available from
|
||||
`python.org <http://docs.python.org/release/2.6.6/tutorial/index.html>`_.
|
||||
`python.org <http://docs.python.org/release/2.7/tutorial/index.html>`_.
|
||||
While we don't think a deep knowledge of Python is necessary to work
|
||||
with Ren'Py, learning about python expressions is helpful.
|
||||
with Ren'Py, the basics of python statements and expressions is
|
||||
often helpful.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
Dealing with Problems
|
||||
=====================
|
||||
|
||||
.. include:: display_problems.rst
|
||||
|
||||
|
||||
64-Bit Linux Problems
|
||||
----------------------
|
||||
|
||||
Ren'Py 6.14.x and 6.15.0-3 were compiled incorrectly, and will often
|
||||
fail to operate on 64-bit Linux computers. The best way to work around
|
||||
this is to download Ren'Py 6.15.4 or later, and use it to run the game::
|
||||
|
||||
|
||||
/path/to/renpy-6.15.4/renpy.sh /path/to/game-with-problems
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
=================
|
||||
Python Statements
|
||||
=================
|
||||
|
||||
Ren'Py is written in the Python programmming language, and includes
|
||||
support for including python code inside Ren'Py scripts. Python
|
||||
support can be used for many things, from setting a flag to creating
|
||||
new displayables. This chapter covers ways in which Ren'Py scripts can
|
||||
directly invoke Ren'Py code, through the various python statements.
|
||||
|
||||
|
||||
.. _python-statement:
|
||||
|
||||
Python
|
||||
------
|
||||
|
||||
The python statement takes a block of python code, and runs that code
|
||||
when control reaches the statement. A basic python statement can be
|
||||
very simple::
|
||||
|
||||
python:
|
||||
flag = True
|
||||
|
||||
Python statements can get more complex, when necessary::
|
||||
|
||||
python:
|
||||
player_health = max(player_health - damage, 0)
|
||||
if enemy_vampire:
|
||||
enemy_health = min(enemy_health + damage, enemy_max_health)
|
||||
|
||||
There are two modifiers to the python statement that change its
|
||||
behavior:
|
||||
|
||||
``hide``
|
||||
|
||||
If given the hide modifier, the python statement will run the
|
||||
code in an anonymous scope. The scope will be lost when the python
|
||||
block terminates.
|
||||
|
||||
This allows python code to use temporary variables that can't be
|
||||
saved - but it means that the store needs to be accessed as fields
|
||||
on the store object, rather than directly.
|
||||
|
||||
``in``
|
||||
|
||||
The ``in`` modifier takes a name. Instead of executing in the
|
||||
default store, the python code will execute in the store that
|
||||
name.
|
||||
|
||||
One-line Python Statement
|
||||
-------------------------
|
||||
|
||||
A common case is to have a single line of python that runs in the
|
||||
default store. For example, a python one-liner can be used to
|
||||
initialize or update a flag. To make writing python one-liners
|
||||
more convenient, there is the one-line python statement.
|
||||
|
||||
The one-line python statement begins with the dollar-sign ($)
|
||||
character, and contains all of the code on that line. Here
|
||||
are some example of python one-liners::
|
||||
|
||||
# Set a flag.
|
||||
$ flag = True
|
||||
|
||||
# Increment a variable.
|
||||
$ rabu_rabu_points += 1
|
||||
|
||||
# Call a function that exposes Ren'Py functionality.
|
||||
$ renpy.movie_cutscene("opening.ogv")
|
||||
|
||||
Python one-liners always run in the default store.
|
||||
|
||||
Named Stores
|
||||
------------
|
||||
|
||||
Named stores provide a way of organizing python code into modules. By
|
||||
placing code in modules, you can minimize the chance of name
|
||||
conflicts.
|
||||
|
||||
Named stores can be accessed by supplying the ``in`` clause to
|
||||
``python`` or ``init python``, code can run accessed in a named
|
||||
store. Each store corresponds to a python module. The default store is
|
||||
``store``, while a named store is accessed a ``store``.`name`. These
|
||||
python modules can be imported using the python import statement,
|
||||
while names in the modules can be imported using the python from
|
||||
statement.
|
||||
|
||||
Named stores participate in save, load, and rollback in the same way
|
||||
that the default store does.
|
||||
|
||||
|
||||
TODO:
|
||||
|
||||
* Note that names beginning with a single _ are reserved for Ren'Py's
|
||||
use.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user