Refactor keygroup into library/
[lsnes.git] / src / core / framebuffer.cpp
blob9ec14b27ad65972f6eaccd51e4d26be9229ff869
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/subtitles.hpp"
5 #include "lua/lua.hpp"
6 #include "core/misc.hpp"
7 #include "core/window.hpp"
8 #include "fonts/wrapper.hpp"
9 #include "library/framebuffer.hpp"
10 #include "library/pixfmt-lrgb.hpp"
12 framebuffer_raw screen_nosignal;
13 framebuffer_raw screen_corrupt;
15 namespace
17 struct render_info
19 framebuffer_raw fbuf;
20 render_queue rq;
21 uint32_t hscl;
22 uint32_t vscl;
23 uint32_t lgap;
24 uint32_t rgap;
25 uint32_t tgap;
26 uint32_t bgap;
29 triplebuffer_logic buffering;
30 render_info buffer1;
31 render_info buffer2;
32 render_info buffer3;
34 render_info& get_write_buffer()
36 unsigned i = buffering.start_write();
37 switch(i) {
38 case 0:
39 return buffer1;
40 case 1:
41 return buffer2;
42 case 2:
43 return buffer3;
44 default:
45 return buffer1;
49 render_info& get_read_buffer()
51 unsigned i = buffering.start_read();
52 switch(i) {
53 case 0:
54 return buffer1;
55 case 1:
56 return buffer2;
57 case 2:
58 return buffer3;
59 default:
60 return buffer1;
64 struct render_list_entry
66 uint32_t codepoint;
67 uint32_t x;
68 uint32_t y;
69 uint32_t scale;
72 struct render_list_entry rl_nosignal[] = {
73 {'N', 4, 168, 7},
74 {'O', 60, 168, 7},
75 {'S', 172, 168, 7},
76 {'I', 228, 168, 7},
77 {'G', 284, 168, 7},
78 {'N', 340, 168, 7},
79 {'A', 396, 168, 7},
80 {'L', 452, 168, 7},
81 {0, 0, 0, 0}
84 struct render_list_entry rl_corrupt[] = {
85 {'S', 88, 56, 7},
86 {'Y', 144, 56, 7},
87 {'S', 200, 56, 7},
88 {'T', 256, 56, 7},
89 {'E', 312, 56, 7},
90 {'M', 368, 56, 7},
91 {'S', 116, 168, 7},
92 {'T', 172, 168, 7},
93 {'A', 224, 168, 7},
94 {'T', 280, 168, 7},
95 {'E', 336, 168, 7},
96 {'C', 60, 280, 7},
97 {'O', 116, 280, 7},
98 {'R', 172, 280, 7},
99 {'R', 228, 280, 7},
100 {'U', 284, 280, 7},
101 {'P', 340, 280, 7},
102 {'T', 396, 280, 7},
103 {0, 0, 0, 0}
106 void draw_special_screen(uint32_t* target, struct render_list_entry* rlist)
108 while(rlist->scale) {
109 auto g = main_font.get_glyph(rlist->codepoint);
110 for(uint32_t j = 0; j < 16; j++) {
111 for(uint32_t i = 0; i < (g.wide ? 16 : 8); i++) {
112 uint32_t slice = g.data[j / (g.wide ? 2 : 4)];
113 uint32_t bit = 31 - ((j % (g.wide ? 2 : 4)) * (g.wide ? 16 : 8) + i);
114 uint32_t value = (slice >> bit) & 1;
115 if(value) {
116 uint32_t basex = rlist->x + rlist->scale * i;
117 uint32_t basey = rlist->y + rlist->scale * j;
118 for(uint32_t j2 = 0; j2 < rlist->scale; j2++)
119 for(uint32_t i2 = 0; i2 < rlist->scale; i2++)
120 target[(basey + j2) * 512 + (basex + i2)] = 0x7FFFF;
124 rlist++;
128 void draw_nosignal(uint32_t* target)
130 for(unsigned i = 0; i < 512 * 448; i++)
131 target[i] = 0x7FC00;
132 draw_special_screen(target, rl_nosignal);
135 void draw_corrupt(uint32_t* target)
137 for(unsigned i = 0; i < 512 * 448; i++)
138 target[i] = 0x7FC00;
139 draw_special_screen(target, rl_corrupt);
142 function_ptr_command<arg_filename> take_screenshot_cmd(lsnes_cmd, "take-screenshot", "Takes a screenshot",
143 "Syntax: take-screenshot <file>\nSaves screenshot to PNG file <file>\n",
144 [](arg_filename file) throw(std::bad_alloc, std::runtime_error) {
145 take_screenshot(file);
146 messages << "Saved PNG screenshot" << std::endl;
149 bool last_redraw_no_lua = true;
152 framebuffer<false> main_screen;
154 void take_screenshot(const std::string& file) throw(std::bad_alloc, std::runtime_error)
156 render_info& ri = get_read_buffer();
157 ri.fbuf.save_png(file);
158 buffering.end_read();
162 void init_special_screens() throw(std::bad_alloc)
164 std::vector<uint32_t> buf;
165 buf.resize(512*448);
167 framebuffer_info inf;
168 inf.type = &_pixel_format_lrgb;
169 inf.mem = reinterpret_cast<char*>(&buf[0]);
170 inf.physwidth = 512;
171 inf.physheight = 448;
172 inf.physstride = 2048;
173 inf.width = 512;
174 inf.height = 448;
175 inf.stride = 2048;
176 inf.offset_x = 0;
177 inf.offset_y = 0;
179 draw_nosignal(&buf[0]);
180 screen_nosignal = framebuffer_raw(inf);
181 draw_corrupt(&buf[0]);
182 screen_corrupt = framebuffer_raw(inf);
185 void redraw_framebuffer(framebuffer_raw& todraw, bool no_lua, bool spontaneous)
187 uint32_t hscl, vscl;
188 auto g = get_scale_factors(todraw.get_width(), todraw.get_height());
189 hscl = g.first;
190 vscl = g.second;
191 render_info& ri = get_write_buffer();
192 ri.rq.clear();
193 struct lua_render_context lrc;
194 lrc.left_gap = 0;
195 lrc.right_gap = 0;
196 lrc.bottom_gap = 0;
197 lrc.top_gap = 0;
198 lrc.queue = &ri.rq;
199 lrc.width = todraw.get_width() * hscl;
200 lrc.height = todraw.get_height() * vscl;
201 if(!no_lua) {
202 lua_callback_do_paint(&lrc, spontaneous);
203 render_subtitles(lrc);
205 ri.fbuf = todraw;
206 ri.hscl = hscl;
207 ri.vscl = vscl;
208 ri.lgap = lrc.left_gap;
209 ri.rgap = lrc.right_gap;
210 ri.tgap = lrc.top_gap;
211 ri.bgap = lrc.bottom_gap;
212 buffering.end_write();
213 information_dispatch::do_screen_update();
214 last_redraw_no_lua = no_lua;
217 void redraw_framebuffer()
219 render_info& ri = get_read_buffer();
220 framebuffer_raw copy = ri.fbuf;
221 buffering.end_read();
222 //Redraws are never spontaneous
223 redraw_framebuffer(copy, last_redraw_no_lua, false);
227 void render_framebuffer()
229 static uint32_t val1, val2, val3, val4;
230 uint32_t nval1, nval2, nval3, nval4;
231 render_info& ri = get_read_buffer();
232 main_screen.reallocate(ri.fbuf.get_width() * ri.hscl + ri.lgap + ri.rgap, ri.fbuf.get_height() * ri.vscl +
233 ri.tgap + ri.bgap);
234 main_screen.set_origin(ri.lgap, ri.tgap);
235 main_screen.copy_from(ri.fbuf, ri.hscl, ri.vscl);
236 ri.rq.run(main_screen);
237 information_dispatch::do_set_screen(main_screen);
238 //We would want divide by 2, but we'll do it ourselves in order to do mouse.
239 keyboard_key* mouse_x = lsnes_kbd.try_lookup_key("mouse_x");
240 keyboard_key* mouse_y = lsnes_kbd.try_lookup_key("mouse_y");
241 keyboard_mouse_calibration xcal;
242 keyboard_mouse_calibration ycal;
243 xcal.offset = ri.lgap;
244 ycal.offset = ri.tgap;
245 if(mouse_x && mouse_x->get_type() == KBD_KEYTYPE_MOUSE)
246 mouse_x->cast_mouse()->set_calibration(xcal);
247 if(mouse_y && mouse_y->get_type() == KBD_KEYTYPE_MOUSE)
248 mouse_y->cast_mouse()->set_calibration(ycal);
249 nval1 = ri.lgap;
250 nval2 = ri.tgap;
251 nval3 = ri.fbuf.get_width() * ri.hscl + ri.rgap;
252 nval4 = ri.fbuf.get_height() * ri.vscl + ri.bgap;
253 val1 = nval1;
254 val2 = nval2;
255 val3 = nval3;
256 val4 = nval4;
257 buffering.end_read();
260 std::pair<uint32_t, uint32_t> get_framebuffer_size()
262 uint32_t v, h;
263 render_info& ri = get_read_buffer();
264 v = ri.fbuf.get_width();
265 h = ri.fbuf.get_height();
266 buffering.end_read();
267 return std::make_pair(h, v);
270 framebuffer_raw get_framebuffer() throw(std::bad_alloc)
272 render_info& ri = get_read_buffer();
273 framebuffer_raw copy = ri.fbuf;
274 buffering.end_read();
275 return copy;
279 triplebuffer_logic::triplebuffer_logic() throw(std::bad_alloc)
281 mut = &mutex::aquire();
282 last_complete_slot = 0;
283 read_active = false;
284 write_active = false;
285 read_active_slot = 0;
286 write_active_slot = 0;
289 triplebuffer_logic::~triplebuffer_logic() throw()
291 delete mut;
294 unsigned triplebuffer_logic::start_write() throw()
296 mutex::holder h(*mut);
297 if(!write_active) {
298 //We need to avoid hitting last complete slot or slot that is active for read.
299 if(last_complete_slot != 0 && read_active_slot != 0)
300 write_active_slot = 0;
301 else if(last_complete_slot != 1 && read_active_slot != 1)
302 write_active_slot = 1;
303 else
304 write_active_slot = 2;
306 write_active++;
307 return write_active_slot;
310 void triplebuffer_logic::end_write() throw()
312 mutex::holder h(*mut);
313 if(!--write_active)
314 last_complete_slot = write_active_slot;
317 unsigned triplebuffer_logic::start_read() throw()
319 mutex::holder h(*mut);
320 if(!read_active)
321 read_active_slot = last_complete_slot;
322 read_active++;
323 return read_active_slot;
326 void triplebuffer_logic::end_read() throw()
328 mutex::holder h(*mut);
329 read_active--;