[9006] | 1 | import os
|
---|
| 2 |
|
---|
| 3 | import numpy
|
---|
| 4 | import pygame
|
---|
| 5 | from gheat import SIZE, base
|
---|
| 6 |
|
---|
| 7 |
|
---|
| 8 | WHITE = (255, 255, 255)
|
---|
| 9 |
|
---|
| 10 |
|
---|
| 11 | # Needed for colors
|
---|
| 12 | # =================
|
---|
| 13 | #
|
---|
| 14 | # http://www.pygame.org/wiki/HeadlessNoWindowsNeeded
|
---|
| 15 | #
|
---|
| 16 | # Beyond what is said there, also set the color depth to 32 bits.
|
---|
| 17 |
|
---|
| 18 | os.environ['SDL_VIDEODRIVER'] = 'dummy'
|
---|
| 19 | pygame.display.init()
|
---|
| 20 | pygame.display.set_mode((1,1), 0, 32)
|
---|
| 21 |
|
---|
| 22 |
|
---|
| 23 | class ColorScheme(base.ColorScheme):
|
---|
| 24 |
|
---|
| 25 | def hook_set(self, fspath):
|
---|
| 26 | colors = pygame.image.load(fspath)
|
---|
| 27 | self.colors = colors = colors.convert_alpha()
|
---|
| 28 | self.color_map = pygame.surfarray.pixels3d(colors)[0]
|
---|
| 29 | self.alpha_map = pygame.surfarray.pixels_alpha(colors)[0]
|
---|
| 30 |
|
---|
| 31 | def hook_build_empty(self, opacity, fspath):
|
---|
| 32 | tile = pygame.Surface((SIZE,SIZE), pygame.SRCALPHA, 32)
|
---|
| 33 | tile.fill(self.color_map[255])
|
---|
| 34 | tile.convert_alpha()
|
---|
| 35 |
|
---|
| 36 | (conf, pixel) = opacity, self.alpha_map[255]
|
---|
| 37 | opacity = int(( (conf/255.0) # from configuration
|
---|
| 38 | * (pixel/255.0) # from per-pixel alpha
|
---|
| 39 | ) * 255)
|
---|
| 40 |
|
---|
| 41 | pygame.surfarray.pixels_alpha(tile)[:,:] = opacity
|
---|
| 42 | pygame.image.save(tile, fspath)
|
---|
| 43 |
|
---|
| 44 |
|
---|
| 45 | class Dot(base.Dot):
|
---|
| 46 | def hook_get(self, fspath):
|
---|
| 47 | img = pygame.image.load(fspath)
|
---|
| 48 | half_size = img.get_size()[0] / 2
|
---|
| 49 | return img, half_size
|
---|
| 50 |
|
---|
| 51 |
|
---|
| 52 | class Tile(base.Tile):
|
---|
| 53 |
|
---|
| 54 | def hook_rebuild(self, points):
|
---|
| 55 | """Given a list of points, save a tile.
|
---|
| 56 |
|
---|
| 57 | This uses the Pygame backend.
|
---|
| 58 |
|
---|
| 59 | Good surfarray tutorial (old but still applies):
|
---|
| 60 |
|
---|
| 61 | http://www.pygame.org/docs/tut/surfarray/SurfarrayIntro.html
|
---|
| 62 |
|
---|
| 63 | Split out to give us better profiling granularity.
|
---|
| 64 |
|
---|
| 65 | """
|
---|
| 66 | tile = self._start()
|
---|
| 67 | tile = self._add_points(tile, points)
|
---|
| 68 | tile = self._trim(tile)
|
---|
| 69 | tile = self._colorize(tile)
|
---|
| 70 | return tile
|
---|
| 71 |
|
---|
| 72 |
|
---|
| 73 | def _start(self):
|
---|
| 74 | tile = pygame.Surface(self.expanded_size, 0, 32)
|
---|
| 75 | tile.fill(WHITE)
|
---|
| 76 | return tile
|
---|
| 77 | #@ why do we get green after this step?
|
---|
| 78 |
|
---|
| 79 |
|
---|
| 80 | def _add_points(self, tile, points):
|
---|
| 81 | for dest in points:
|
---|
| 82 | tile.blit(self.dot, dest, None, pygame.BLEND_MULT)
|
---|
| 83 | return tile
|
---|
| 84 |
|
---|
| 85 |
|
---|
| 86 | def _trim(self, tile):
|
---|
| 87 | tile = tile.subsurface(self.pad, self.pad, SIZE, SIZE).copy()
|
---|
| 88 | #@ pygame.transform.chop says this or blit; this is plenty fast
|
---|
| 89 | return tile
|
---|
| 90 |
|
---|
| 91 |
|
---|
| 92 | def _colorize(self, tile):
|
---|
| 93 |
|
---|
| 94 | # Invert/colorize
|
---|
| 95 | # ===============
|
---|
| 96 | # The way this works is that we loop through all pixels in the image,
|
---|
| 97 | # and set their color and their transparency based on an index image.
|
---|
| 98 | # The index image can be as wide as we want; we only look at the first
|
---|
| 99 | # column of pixels. This first column is considered a mapping of 256
|
---|
| 100 | # gray-scale intensity values to color/alpha.
|
---|
| 101 |
|
---|
| 102 | # Optimized: I had the alpha computation in a separate function because
|
---|
| 103 | # I'm also using it above in ColorScheme (cause I couldn't get set_alpha
|
---|
| 104 | # working). The inner loop runs 65536 times, and just moving the
|
---|
| 105 | # computation out of a function and inline into the loop sped things up
|
---|
| 106 | # about 50%. It sped it up another 50% to cache the values, since each
|
---|
| 107 | # of the 65536 variables only ever takes one of 256 values. Not super
|
---|
| 108 | # fast still, but more reasonable (1.5 seconds instead of 12).
|
---|
| 109 | #
|
---|
| 110 | # I would expect that precomputing the dictionary at start-up time
|
---|
| 111 | # should give us another boost, but it slowed us down again. Maybe
|
---|
| 112 | # since with precomputation we have to calculate more than we use, the
|
---|
| 113 | # size of the dictionary made a difference? Worth exploring ...
|
---|
| 114 |
|
---|
| 115 | _computed_opacities = dict()
|
---|
| 116 |
|
---|
| 117 | tile = tile.convert_alpha(self.color_scheme.colors)
|
---|
| 118 | tile.lock()
|
---|
| 119 | pix = pygame.surfarray.pixels3d(tile)
|
---|
| 120 | alp = pygame.surfarray.pixels_alpha(tile)
|
---|
| 121 | for x in range(SIZE):
|
---|
| 122 | for y in range(SIZE):
|
---|
| 123 | key = pix[x,y,0]
|
---|
| 124 |
|
---|
| 125 | conf, pixel = self.opacity, self.color_scheme.alpha_map[key]
|
---|
| 126 | if (conf, pixel) not in _computed_opacities:
|
---|
| 127 | opacity = int(( (conf/255.0) # from configuration
|
---|
| 128 | * (pixel/255.0) # from per-pixel alpha
|
---|
| 129 | ) * 255)
|
---|
| 130 | _computed_opacities[(conf, pixel)] = opacity
|
---|
| 131 |
|
---|
| 132 | pix[x,y] = self.color_scheme.color_map[key]
|
---|
| 133 | alp[x,y] = _computed_opacities[(conf, pixel)]
|
---|
| 134 |
|
---|
| 135 | tile.unlock()
|
---|
| 136 |
|
---|
| 137 | return tile
|
---|
| 138 |
|
---|
| 139 |
|
---|
| 140 | def save(self):
|
---|
| 141 | pygame.image.save(self.img, self.fspath)
|
---|
| 142 |
|
---|
| 143 |
|
---|