summaryrefslogtreecommitdiff
path: root/unrpyc/renpy/display/module.py
blob: 1bf66e0e8f1a4ac06b3009eacab1440515e4591a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# This file mediates access to the _renpy module, which is a C module that
# allows us to enhance the feature set of pygame in a renpy specific way.

VERSION = (6, 12, 0)

import renpy.display
import pygame; pygame # prevents pyflakes warning.

import sys

try:
    import _renpy
    version = _renpy.version()

    if version != VERSION:
        print("Found Ren'Py module version %s, while expecting %s." % (
            ".".join(str(i) for i in version),
            ".".join(str(i) for i in VERSION),
            ))

        print("Trying to run anyway, but you should expect errors.", file=sys.stderr)
        
except:
    print("The _renpy module was not found. Please read module/README.txt for", file=sys.stderr)
    print("more information.", file=sys.stderr)

    sys.exit(-1)

def convert_and_call(function, src, dst, *args):
    """
    This calls the function with the source and destination
    surface. The surfaces must have the same alpha.

    If the surfaces are not 24 or 32 bits per pixel, or don't have the
    same format, they are converted and then converted back.
    """

    # Now that all surfaces are 32bpp, this function doesn't do much
    # of anything anymore.
    
    if (dst.get_masks()[3] != 0) != (src.get_masks()[3] != 0):
        raise Exception("Surface alphas do not match.")

    function(src, dst, *args)


def pixellate(src, dst, avgwidth, avgheight, outwidth, outheight):
    """
    This pixellates the source surface. First, every pixel in the
    source surface is projected onto a virtual surface, such that
    the average value of every avgwidth x avgheight pixels becomes
    one virtual pixel. It then gets projected back onto the
    destination surface at a ratio of one virtual pixel to every
    outwidth x outheight destination pixels.

    If either src or dst is not a 24 or 32 bit surface, they are
    converted... but that may be a significant performance hit.

    The two surfaces must either have the same alpha or no alpha.
    """

    convert_and_call(_renpy.pixellate,
                     src, dst,
                     avgwidth, avgheight,
                     outwidth, outheight)


def scale(s, size):
    """
    Scales down the supplied pygame surface by the given X and Y
    factors.

    Always works, but may not be high quality.
    """

    d = renpy.display.pgrender.surface(size, True)

    bilinear_scale(s, d)
    
    return d


# What we have here are a pair of tables mapping masks to byte offsets
# for 24 and 32 bpp modes. We represent 0xff000000 as positive and negative
# numbers so that it doesn't yield a warning, and so that it works on
# 32 and 64 bit platforms.
if sys.byteorder == 'big':
    bo32 = { 255 : 3, 65280 : 2, 16711680 : 1, 4278190080 : 0, -16777216 : 0, }
else:
    bo32 = { 255 : 0, 65280 : 1, 16711680 : 2, 4278190080 : 3, -16777216 : 3, }

bo_cache = None

def byte_offset(src):
    """
    Given the surface src, returns a 4-tuple giving the byte offsets
    for the red, green, blue, and alpha components of the pixels in
    the surface. If a component doesn't exist, None is returned.
    """

    global bo_cache
    
    if bo_cache is None:
        bo_cache = [ bo32[i] for i in src.get_masks() ]
        
    return bo_cache

def endian_order(src, r, g, b, a):

    if bo_cache is None:
        byte_offset(src)

    rv = [ a, a, a, a ]

    for i, index_i in zip((r, g, b, a), bo_cache):
        rv[index_i] = i

    return rv



def linmap(src, dst, rmap, gmap, bmap, amap):
    """
    This maps the colors between two surfaces. The various map
    parameters should be fixed-point integers, with 1.0 == 256.
    """

    convert_and_call(_renpy.linmap,
                     src, dst,
                     *endian_order(dst, rmap, gmap, bmap, amap))


save_png = _renpy.save_png

def map(src, dst, rmap, gmap, bmap, amap): #@ReservedAssignment
    """
    This maps the colors between two surfaces. The various map
    parameters must be 256 character long strings, with the value
    of a character at a given offset being what a particular pixel
    component value is mapped to.
    """

    convert_and_call(_renpy.map,
                     src, dst,
                     *endian_order(dst, rmap, gmap, bmap, amap))



def twomap(src, dst, white, black):
    """
    Given colors for white and black, linearly maps things
    appropriately, taking the alpha channel from white.
    """

    wr = white[0]
    wg = white[1]
    wb = white[2]
    wa = white[3]

    br = black[0]
    bg = black[1]
    bb = black[2]

    ramp = renpy.display.im.ramp

    if br == 0 and bg == 0 and bb == 0:
        linmap(src, dst,
               wr + 1,
               wg + 1,
               wb + 1,
               wa + 1)
    else:
        list(map(src, dst,
            ramp(br, wr),
            ramp(bg, wg),
            ramp(bb, wb),
            ramp(0, wa)))


def alpha_munge(src, dst, amap):
    """
    This samples the red channel from src, maps it through amap, and
    place it into the alpha channel of amap.
    """

    if src.get_size() != dst.get_size():
        return

    red = byte_offset(src)[0]
    alpha = byte_offset(dst)[3]

    if red is not None and alpha is not None:
        _renpy.alpha_munge(src, dst, red, alpha, amap)        


def bilinear_scale(src, dst, sx=0, sy=0, sw=None, sh=None, dx=0, dy=0, dw=None, dh=None, precise=0):

    if sw is None:
        sw, sh = src.get_size()
    if dw is None:
        dw, dh = dst.get_size()

    while True:

        if sw <= dw * 2 and sh <= dh * 2:
            break

        nsw = max(sw / 2, dw)
        nsh = max(sh / 2, dh)

        nsrc = renpy.display.pgrender.surface((nsw, nsh), src.get_masks()[3])

        _renpy.bilinear(src, nsrc, sx, sy, sw, sh, precise=precise)

        sx = 0
        sy = 0
        sw = nsw
        sh = nsh
        src = nsrc

    _renpy.bilinear(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, precise=precise)
        

transform = _renpy.transform
    
# Note: Blend requires all surfaces to be the same size.    
blend = _renpy.blend
    
def imageblend(a, b, dst, img, amap):        
    alpha = byte_offset(img)[3]
    _renpy.imageblend(a, b, dst, img, alpha, amap)


def colormatrix(src, dst, matrix):
    c = [ matrix[0:5], matrix[5:10], matrix[10:15], matrix[15:20] ]
    offs = byte_offset(src)

    o = [ None ] * 4
    for i in range(0, 4):
        o[offs[i]] = i

    _renpy.colormatrix(src, dst,
                       c[o[0]][o[0]], c[o[0]][o[1]], c[o[0]][o[2]], c[o[0]][o[3]], c[o[0]][4],    
                       c[o[1]][o[0]], c[o[1]][o[1]], c[o[1]][o[2]], c[o[1]][o[3]], c[o[1]][4],    
                       c[o[2]][o[0]], c[o[2]][o[1]], c[o[2]][o[2]], c[o[2]][o[3]], c[o[2]][4],    
                       c[o[3]][o[0]], c[o[3]][o[1]], c[o[3]][o[2]], c[o[3]][o[3]], c[o[3]][4])


def subpixel(src, dst, x, y):

    shift = src.get_shifts()[3]
    _renpy.subpixel(src, dst, x, y, shift)