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 |
|
---|