OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "experimental/conways_life/life.h" | |
6 | |
7 #include <stdio.h> | |
8 #include <algorithm> | |
9 #include <cmath> | |
10 #include <string> | |
11 | |
12 #include "experimental/conways_life/scoped_pixel_lock.h" | |
13 #include "experimental/conways_life/threading/scoped_mutex_lock.h" | |
14 #include "ppapi/cpp/size.h" | |
15 | |
16 namespace { | |
17 const unsigned int kInitialRandSeed = 0xC0DE533D; | |
18 const int kMaxNeighbourCount = 8; | |
19 // Offests into the autmaton rule lookup table. | |
20 const size_t kKeepAliveRuleOffset = 9; | |
21 const size_t kBirthRuleOffset = 0; | |
22 | |
23 inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { | |
24 return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); | |
25 } | |
26 | |
27 // Map of neighboring colors. | |
28 const uint32_t kNeighborColors[] = { | |
29 MakeRGBA(0xFF, 0xFF, 0xFF, 0xFF), // Dead with 0 neighbours. | |
30 MakeRGBA(0xE0, 0xE0, 0xE0, 0xFF), // Dead with 1 neighbour. | |
31 MakeRGBA(0xC0, 0xC0, 0xC0, 0xFF), // 2 neighbours. | |
32 MakeRGBA(0xA0, 0xA0, 0xA0, 0xFF), // 3 | |
33 MakeRGBA(0x80, 0x80, 0x80, 0xFF), // 4 | |
34 MakeRGBA(0x60, 0x60, 0x60, 0xFF), // 5 | |
35 MakeRGBA(0x40, 0x40, 0x40, 0xFF), // 6 | |
36 MakeRGBA(0x20, 0x20, 0x20, 0xFF), // 7 | |
37 MakeRGBA(0x10, 0x10, 0x10, 0xFF), // 8 | |
38 MakeRGBA(0x00, 0x00, 0x00, 0xFF), // Alive with 0 neighbours. | |
39 MakeRGBA(0x10, 0x10, 0x10, 0xFF), // Alive with 1 neighbours. | |
40 MakeRGBA(0x20, 0x20, 0x20, 0xFF), // 2 neighbours. | |
41 MakeRGBA(0x30, 0x30, 0x30, 0xFF), // 3 | |
42 MakeRGBA(0x40, 0x40, 0x40, 0xFF), // 4 | |
43 MakeRGBA(0x60, 0x60, 0x60, 0xFF), // 5 | |
44 MakeRGBA(0x80, 0x80, 0x80, 0xFF), // 6 | |
45 MakeRGBA(0xC0, 0xC0, 0xC0, 0xFF), // 7 | |
46 MakeRGBA(0xF0, 0xF0, 0xF0, 0xFF) // 8 | |
47 }; | |
48 } // namespace | |
49 | |
50 namespace life { | |
51 Life::Life() : life_simulation_thread_(NULL), | |
52 sim_state_condition_(kSimulationStopped), | |
53 width_(0), | |
54 height_(0), | |
55 simulation_mode_(kPaused), | |
56 random_bits_(kInitialRandSeed), | |
57 cell_in_(NULL), | |
58 cell_out_(NULL) { | |
59 pthread_mutex_init(&life_simulation_mutex_, NULL); | |
60 // Set up the default life rules table. |life_rules_table_| is a look-up | |
61 // table, index by the number of living neighbours of a cell. Indices [0..8] | |
62 // represent the rules when the cell being examined is dead; indices [9..17] | |
63 // represnt the rules when the cell is alive. | |
64 life_rules_table_.resize(kMaxNeighbourCount * 2 + 1); | |
65 std::fill(&life_rules_table_[0], | |
66 &life_rules_table_[0] + life_rules_table_.size(), | |
67 0); | |
68 life_rules_table_[kBirthRuleOffset + 3] = 1; | |
69 life_rules_table_[kKeepAliveRuleOffset + 2] = 1; | |
70 life_rules_table_[kKeepAliveRuleOffset + 3] = 1; | |
71 } | |
72 | |
73 Life::~Life() { | |
74 set_is_simulation_running(false); | |
75 if (life_simulation_thread_) { | |
76 pthread_join(life_simulation_thread_, NULL); | |
77 } | |
78 DeleteCells(); | |
79 pthread_mutex_destroy(&life_simulation_mutex_); | |
80 } | |
81 | |
82 void Life::StartSimulation() { | |
83 pthread_create(&life_simulation_thread_, NULL, LifeSimulation, this); | |
84 } | |
85 | |
86 Life::SimulationMode Life::WaitForRunMode() { | |
87 simulation_mode_.LockWhenNotCondition(kPaused); | |
88 simulation_mode_.Unlock(); | |
89 return simulation_mode(); | |
90 } | |
91 | |
92 void Life::SetAutomatonRules(const std::string& rule_string) { | |
93 if (rule_string.size() == 0) | |
94 return; | |
95 // Separate the birth rule from the keep-alive rule. | |
96 size_t slash_pos = rule_string.find('/'); | |
97 if (slash_pos == std::string::npos) | |
98 return; | |
99 std::string keep_alive_rule = rule_string.substr(0, slash_pos); | |
100 std::string birth_rule = rule_string.substr(slash_pos + 1); | |
101 threading::ScopedMutexLock scoped_mutex(&life_simulation_mutex_); | |
102 if (!scoped_mutex.is_valid()) | |
103 return; | |
104 std::fill(&life_rules_table_[0], | |
105 &life_rules_table_[0] + life_rules_table_.size(), | |
106 0); | |
107 SetRuleFromString(kBirthRuleOffset, birth_rule); | |
108 SetRuleFromString(kKeepAliveRuleOffset, keep_alive_rule); | |
109 } | |
110 | |
111 void Life::SetRuleFromString(size_t rule_offset, | |
112 const std::string& rule_string) { | |
113 for (size_t i = 0; i < rule_string.size(); ++i) { | |
114 size_t rule_index = rule_string[i] - '0'; | |
115 life_rules_table_[rule_offset + rule_index] = 1; | |
116 } | |
117 } | |
118 | |
119 void Life::Resize(int width, int height) { | |
120 DeleteCells(); | |
121 width_ = std::max(width, 0); | |
122 height_ = std::max(height, 0); | |
123 size_t size = width * height; | |
124 cell_in_ = new uint8_t[size]; | |
125 cell_out_ = new uint8_t[size]; | |
126 ClearCells(); | |
127 } | |
128 | |
129 void Life::DeleteCells() { | |
130 delete[] cell_in_; | |
131 delete[] cell_out_; | |
132 cell_in_ = cell_out_ = NULL; | |
133 } | |
134 | |
135 void Life::ClearCells() { | |
136 const size_t size = width() * height(); | |
137 if (cell_in_) | |
138 std::fill(cell_in_, cell_in_ + size, 0); | |
139 if (cell_out_) | |
140 std::fill(cell_out_, cell_out_ + size, 0); | |
141 } | |
142 | |
143 void Life::PutStampAtPoint(const Stamp& stamp, const pp::Point& point) { | |
144 // Note: do not acquire the pixel lock here, because stamping is done in the | |
145 // UI thread. | |
146 stamp.StampAtPointInBuffers( | |
147 point, | |
148 shared_pixel_buffer_->PixelBufferNoLock(), | |
149 cell_in_, | |
150 pp::Size(width(), height())); | |
151 } | |
152 | |
153 void Life::AddRandomSeed() { | |
154 threading::ScopedMutexLock scoped_mutex(&life_simulation_mutex_); | |
155 if (!scoped_mutex.is_valid()) | |
156 return; | |
157 if (cell_in_ == NULL || cell_out_ == NULL) | |
158 return; | |
159 const int sim_height = height(); | |
160 const int sim_width = width(); | |
161 for (int i = 0; i < sim_width; ++i) { | |
162 cell_in_[i] = random_bits_.value(); | |
163 cell_in_[i + (sim_height - 1) * sim_width] = random_bits_.value(); | |
164 } | |
165 for (int i = 0; i < sim_height; ++i) { | |
166 cell_in_[i * sim_width] = random_bits_.value(); | |
167 cell_in_[i * sim_width + (sim_width - 1)] = random_bits_.value(); | |
168 } | |
169 } | |
170 | |
171 void Life::UpdateCells() { | |
172 ScopedPixelLock scoped_pixel_lock(shared_pixel_buffer_); | |
173 uint32_t* pixel_buffer = scoped_pixel_lock.pixels(); | |
174 if (pixel_buffer == NULL || cell_in_ == NULL || cell_out_ == NULL) { | |
175 // Note that if the pixel buffer never gets initialized, this won't ever | |
176 // paint anything. Which is probably the right thing to do. Also, this | |
177 // clause means that the image will not get the very first few sim cells, | |
178 // since it's possible that this thread starts before the pixel buffer is | |
179 // initialized. | |
180 return; | |
181 } | |
182 const int sim_height = height(); | |
183 const int sim_width = width(); | |
184 // Do neighbor sumation; apply rules, output pixel color. | |
185 for (int y = 1; y < (sim_height - 1); ++y) { | |
186 uint8_t *src = cell_in_ + 1 + y * sim_width; | |
187 uint8_t *src_above = src - sim_width; | |
188 uint8_t *src_below = src + sim_width; | |
189 uint8_t *dst = cell_out_ + 1 + y * sim_width; | |
190 uint32_t *scanline = pixel_buffer + 1 + y * sim_width; | |
191 for (int x = 1; x < (sim_width - 1); ++x) { | |
192 // Build sum to get a neighbour count. By multiplying the current | |
193 // (center) cell by 9, this provides indices [0..8] that relate to the | |
194 // cases when the current cell is dead, and indices [9..17] that relate | |
195 // to the cases when the current cell is alive. | |
196 int count = *(src_above - 1) + *src_above + *(src_above + 1) + | |
197 *(src - 1) + *src * kKeepAliveRuleOffset + *(src + 1) + | |
198 *(src_below - 1) + *src_below + *(src_below + 1); | |
199 *dst++ = life_rules_table_[count]; | |
200 *scanline++ = kNeighborColors[count]; | |
201 ++src; | |
202 ++src_above; | |
203 ++src_below; | |
204 } | |
205 } | |
206 } | |
207 | |
208 void Life::Swap() { | |
209 threading::ScopedMutexLock scoped_mutex(&life_simulation_mutex_); | |
210 if (!scoped_mutex.is_valid()) { | |
211 return; | |
212 } | |
213 if (cell_in_ == NULL || cell_out_ == NULL) { | |
214 return; | |
215 } | |
216 std::swap(cell_in_, cell_out_); | |
217 } | |
218 | |
219 void* Life::LifeSimulation(void* param) { | |
220 Life* life = static_cast<Life*>(param); | |
221 // Run the Life simulation in an endless loop. Shut this down when | |
222 // is_simulation_running() returns |false|. | |
223 life->set_is_simulation_running(true); | |
224 while (life->is_simulation_running()) { | |
225 SimulationMode sim_mode = life->WaitForRunMode(); | |
226 if (sim_mode == kRunRandomSeed) { | |
227 life->AddRandomSeed(); | |
228 } | |
229 life->UpdateCells(); | |
230 life->Swap(); | |
231 } | |
232 return NULL; | |
233 } | |
234 | |
235 uint8_t Life::RandomBitGenerator::value() { | |
236 return static_cast<uint8_t>(rand_r(&random_bit_seed_) & 1); | |
237 } | |
238 | |
239 } // namespace life | |
240 | |
OLD | NEW |