# Copyright 2004-2013 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import renpy.display from renpy.display.render import render, Render, Matrix2D # This file contains displayables that are image-like, because they take # up a rectangular area of the screen, and do not respond to input. class Solid(renpy.display.core.Displayable): """ :doc: disp_imagelike A displayable that fills the area its assigned with `color`. :: image white = Solid("#fff") """ def __init__(self, color, **properties): super(Solid, self).__init__(**properties) if color is not None: self.color = renpy.easy.color(color) else: self.color = None def visit(self): return [ ] def render(self, width, height, st, at): color = self.color or self.style.color rv = Render(width, height) if color is None or width <= 0 or height <= 0: return rv SIZE = 10 if width < SIZE or height < SIZE: tex = renpy.display.draw.solid_texture(width, height, color) else: tex = renpy.display.draw.solid_texture(SIZE, SIZE, color) rv.forward = Matrix2D(1.0 * SIZE / width, 0, 0, 1.0 * SIZE / height) rv.reverse = Matrix2D(1.0 * width / SIZE, 0, 0, 1.0 * height / SIZE) rv.blit(tex, (0, 0)) return rv class Frame(renpy.display.core.Displayable): """ :doc: disp_imagelike :args: (image, xborder, yborder, tile=False, **properties) A displayable that resizes an image to fill the available area, while preserving the width and height of its borders. is often used as the background of a window or button. .. figure:: frame_example.png Using a frame to resize an image to double its size. `image` An image manipulator that will be resized by this frame. `left` The size of the border on the left side. `top` The size of the border on the top. `right` The size of the border on the right side. If None, defaults to `left`. `bottom` The side of the border on the bottom. If None, defaults to `top`. `tile` If true, tiling is used to resize sections of the image, rather than scaling. :: # Resize the background of the text window if it's too small. init python: style.window.background = Frame("frame.png", 10, 10) """ __version__ = 1 def after_upgrade(self, version): if version < 2: self.left = self.xborder self.right = self.xborder self.top = self.yborder self.bottom = self.yborder def __init__(self, image, left, top, right=None, bottom=None, bilinear=True, tile=False, **properties): super(Frame, self).__init__(**properties) self.image = renpy.easy.displayable(image) self.tile = tile if right is None: right = left if bottom is None: bottom = top self.left = left self.top = top self.right = right self.bottom = bottom def render(self, width, height, st, at): crend = render(self.image, width, height, st, at) sw, sh = crend.get_size() sw = int(sw) sh = int(sh) dw = int(width) dh = int(height) bw = self.left + self.right bh = self.top + self.bottom xborder = min(bw, sw - 2, dw) if xborder: left = self.left * xborder / bw right = self.right * xborder / bw else: left = 0 right = 0 yborder = min(bh, sh - 2, dh) if yborder: top = self.top * yborder / bh bottom = self.bottom * yborder / bh else: top = 0 bottom = 0 if renpy.display.draw.info["renderer"] == "sw": return self.sw_render(crend, dw, dh, left, top, right, bottom) def draw(x0, x1, y0, y1): # Compute the coordinates of the left, right, top, and # bottom sides of the region, for both the source and # destination surfaces. # left side. if x0 >= 0: dx0 = x0 sx0 = x0 else: dx0 = dw + x0 sx0 = sw + x0 # right side. if x1 > 0: dx1 = x1 sx1 = x1 else: dx1 = dw + x1 sx1 = sw + x1 # top side. if y0 >= 0: dy0 = y0 sy0 = y0 else: dy0 = dh + y0 sy0 = sh + y0 # bottom side if y1 > 0: dy1 = y1 sy1 = y1 else: dy1 = dh + y1 sy1 = sh + y1 # Quick exit. if sx0 == sx1 or sy0 == sy1: return # Compute sizes. csw = sx1 - sx0 csh = sy1 - sy0 cdw = dx1 - dx0 cdh = dy1 - dy0 if csw <= 0 or csh <= 0 or cdh <= 0 or cdw <= 0: return # Get a subsurface. cr = crend.subsurface((sx0, sy0, csw, csh)) # Scale or tile if we have to. if csw != cdw or csh != cdh: if self.tile: newcr = Render(cdw, cdh) newcr.clipping = True for x in range(0, cdw, csw): for y in range(0, cdh, csh): newcr.blit(cr, (x, y)) cr = newcr else: newcr = Render(cdw, cdh) newcr.forward = Matrix2D(1.0 * csw / cdw, 0, 0, 1.0 * csh / cdh) newcr.reverse = Matrix2D(1.0 * cdw / csw, 0, 0, 1.0 * cdh / csh) newcr.blit(cr, (0, 0)) cr = newcr # Blit. rv.blit(cr, (dx0, dy0)) return rv = Render(dw, dh) self.draw_pattern(draw, left, top, right, bottom) return rv def draw_pattern(self, draw, left, top, right, bottom): # Top row. if top: if left: draw(0, left, 0, top) draw(left, -right, 0, top) if right: draw(-right, 0, 0, top) # Middle row. if left: draw(0, left, top, -bottom) draw(left, -right, top, -bottom) if right: draw(-right, 0, top, -bottom) # Bottom row. if bottom: if left: draw(0, left, -bottom, 0) draw(left, -right, -bottom, 0) if right: draw(-right, 0, -bottom, 0) def sw_render(self, crend, dw, dh, left, top, right, bottom): source = crend.render_to_texture(True) sw, sh = source.get_size() dest = renpy.display.swdraw.surface(dw, dh, True) rv = dest def draw(x0, x1, y0, y1): # Compute the coordinates of the left, right, top, and # bottom sides of the region, for both the source and # destination surfaces. # left side. if x0 >= 0: dx0 = x0 sx0 = x0 else: dx0 = dw + x0 sx0 = sw + x0 # right side. if x1 > 0: dx1 = x1 sx1 = x1 else: dx1 = dw + x1 sx1 = sw + x1 # top side. if y0 >= 0: dy0 = y0 sy0 = y0 else: dy0 = dh + y0 sy0 = sh + y0 # bottom side if y1 > 0: dy1 = y1 sy1 = y1 else: dy1 = dh + y1 sy1 = sh + y1 # Quick exit. if sx0 == sx1 or sy0 == sy1 or dx1 <= dx0 or dy1 <= dy0: return # Compute sizes. srcsize = (sx1 - sx0, sy1 - sy0) dstsize = (int(dx1 - dx0), int(dy1 - dy0)) # Get a subsurface. surf = source.subsurface((sx0, sy0, srcsize[0], srcsize[1])) # Scale or tile if we have to. if dstsize != srcsize: if self.tile: tilew, tileh = srcsize dstw, dsth = dstsize surf2 = renpy.display.pgrender.surface_unscaled(dstsize, surf) for y in range(0, dsth, tileh): for x in range(0, dstw, tilew): surf2.blit(surf, (x, y)) surf = surf2 else: surf2 = renpy.display.scale.real_transform_scale(surf, dstsize) surf = surf2 # Blit. dest.blit(surf, (dx0, dy0)) self.draw_pattern(draw, left, top, right, bottom) rrv = renpy.display.render.Render(dw, dh) rrv.blit(rv, (0, 0)) rrv.depends_on(crend) # And, finish up. return rrv def visit(self): return [ self.image ]