diff options
Diffstat (limited to 'unrpyc/renpy/display/imagemap.py')
-rw-r--r-- | unrpyc/renpy/display/imagemap.py | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/unrpyc/renpy/display/imagemap.py b/unrpyc/renpy/display/imagemap.py new file mode 100644 index 0000000..380bf80 --- /dev/null +++ b/unrpyc/renpy/display/imagemap.py @@ -0,0 +1,233 @@ +# 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 handles imagemap caching. + +import pygame +import renpy.display + +from renpy.display.render import render + +import hashlib +import os + +# A list of cache images we've already written. +cached = set() + +class ImageMapCrop(renpy.display.core.Displayable): + """ + This handles the cropping of uncached imagemap components. + """ + + def __init__(self, child, rect): + super(ImageMapCrop, self).__init__() + + self.child = child + self.rect = rect + + def visit(self): + return [ self.child ] + + def render(self, width, height, st, at): + cr = render(self.child, width, height, st, at) + return cr.subsurface(self.rect) + + +class ImageCacheCrop(renpy.display.core.Displayable): + """ + This handles the cropping of an imagemap component. + """ + + def __init__(self, cache, index): + super(ImageCacheCrop, self).__init__() + + # The cache object we're associated with. + self.cache = cache + + # The index of + self.index = index + + def visit(self): + return self.cache.visit(self.index) + + def render(self, width, height, st, at): + return self.cache.render(self.index, width, height, st, at) + +class ImageMapCache(renpy.object.Object): + + def __init__(self, enable): + self.md5 = hashlib.md5() + + # A list of (image, rect) tuples. The index in this list is used + # as a unique identifier for an ImageCacheCrop object. + self.imagerect = [ ] + + # A map from (image, rect) to ImageCacheCrop object. + self.hotspots = { } + + # A list of (width, height, index) tuples. + self.areas = [ ] + + # The image containing our children. + self.cache = None + + # A list that, for each hotspot, gives the rectangle in the cache + # image corresponding to that hotspot. + self.cache_rect = None + + # The size of the cache. + self.cache_width = None + self.cache_height = None + + self.enable = enable + + def visit(self, index): + if self.cache is not None: + return [ self.cache ] + else: + return [ self.imagerect[index][0] ] + + def crop(self, d, rect): + if not isinstance(d, renpy.display.im.ImageBase) or \ + not renpy.config.imagemap_cache or \ + not self.enable: + return ImageMapCrop(d, rect) + + key = (d, rect) + rv = self.hotspots.get(key, None) + if rv is not None: + return rv + + self.md5.update(repr(d.identity)) + self.md5.update(repr(d.identity)) + + index = len(self.imagerect) + rv = ImageCacheCrop(self, index) + + self.imagerect.append(key) + self.hotspots[key] = rv + self.areas.append((rect[2] + 2, rect[3] + 2, index)) + + return rv + + def layout(self): + self.areas.sort() + self.areas.reverse() + self.cache_rect = [ None ] * len(self.areas) + + # The width of the cache image. + width = self.areas[0][0] + + x = 0 + y = 0 + line_height = 0 + + for w, h, i in self.areas: + + if x + w > width: + y += line_height + line_height = 0 + x = 0 + + self.cache_rect[i] = (x+1, y+1, w-2, h-2) + + x += w + if line_height < h: + line_height = h + + self.cache_width = width + self.cache_height = y + line_height + + def write_cache(self, filename): + + if filename in cached: + return + + cached.add(filename) + + # If all of our dependencies are of the same age or less, + # we don't need to rebuild the cache. + + if renpy.loader.loadable(filename): + d_set = set() + mtime = 0 + + for i, rect in self.imagerect: + if i in d_set: + continue + + d_set.add(i) + mtime = max(i.get_mtime(), mtime) + + if renpy.loader.get_mtime(filename) >= mtime: + return + + fn = os.path.join(renpy.config.gamedir, filename) + dir = os.path.dirname(fn) #@ReservedAssignment + + if not os.path.exists(dir): + os.makedirs(dir) + + cache = pygame.Surface((self.cache_width, self.cache_height), pygame.SRCALPHA, 32) + + for i, (d, rect) in enumerate(self.imagerect): + x, y, _w, _h = self.cache_rect[i] + + surf = renpy.display.im.cache.get(d).subsurface(rect) + cache.blit(surf, (x, y)) + + pygame.image.save(cache, renpy.exports.fsencode(fn)) + + def finish(self): + if not self.areas: + return + + filename = "im-%s.png" % (self.md5.hexdigest()) + + if renpy.game.preferences.language: + filename = renpy.game.preferences.language + "-" + filename + + filename = "cache/" + filename + + self.md5 = None + + self.layout() + + if renpy.config.developer: + try: + self.write_cache(filename) + except: + pass + + if renpy.loader.loadable(filename): + self.cache = renpy.display.im.Image(filename) + + + def render(self, index, width, height, st, at): + if self.cache is None: + d, rect = self.imagerect[index] + return render(d, width, height, st, at).subsurface(rect) + + return render(self.cache, width, height, st, at).subsurface(self.cache_rect[index]) + + + + |