JSON-based controller descriptions
[lsnes.git] / src / core / framebuffer.cpp
blob418952d1be6d5a82241741a6ed46a3a66150aa08
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 "core/moviedata.hpp"
9 #include "core/moviefile.hpp"
10 #include "fonts/wrapper.hpp"
11 #include "library/framebuffer.hpp"
12 #include "library/pixfmt-lrgb.hpp"
14 framebuffer_raw screen_corrupt;
16 namespace
18 struct render_info
20 framebuffer_raw fbuf;
21 render_queue rq;
22 uint32_t hscl;
23 uint32_t vscl;
24 uint32_t lgap;
25 uint32_t rgap;
26 uint32_t tgap;
27 uint32_t bgap;
30 triplebuffer_logic buffering;
31 render_info buffer1;
32 render_info buffer2;
33 render_info buffer3;
35 render_info& get_write_buffer()
37 unsigned i = buffering.start_write();
38 switch(i) {
39 case 0:
40 return buffer1;
41 case 1:
42 return buffer2;
43 case 2:
44 return buffer3;
45 default:
46 return buffer1;
50 render_info& get_read_buffer()
52 unsigned i = buffering.start_read();
53 switch(i) {
54 case 0:
55 return buffer1;
56 case 1:
57 return buffer2;
58 case 2:
59 return buffer3;
60 default:
61 return buffer1;
65 struct render_list_entry
67 uint32_t codepoint;
68 uint32_t x;
69 uint32_t y;
70 uint32_t scale;
73 struct render_list_entry rl_corrupt[] = {
74 {'S', 88, 56, 7},
75 {'Y', 144, 56, 7},
76 {'S', 200, 56, 7},
77 {'T', 256, 56, 7},
78 {'E', 312, 56, 7},
79 {'M', 368, 56, 7},
80 {'S', 116, 168, 7},
81 {'T', 172, 168, 7},
82 {'A', 224, 168, 7},
83 {'T', 280, 168, 7},
84 {'E', 336, 168, 7},
85 {'C', 60, 280, 7},
86 {'O', 116, 280, 7},
87 {'R', 172, 280, 7},
88 {'R', 228, 280, 7},
89 {'U', 284, 280, 7},
90 {'P', 340, 280, 7},
91 {'T', 396, 280, 7},
92 {0, 0, 0, 0}
95 void draw_special_screen(uint32_t* target, struct render_list_entry* rlist)
97 while(rlist->scale) {
98 auto g = main_font.get_glyph(rlist->codepoint);
99 for(uint32_t j = 0; j < 16; j++) {
100 for(uint32_t i = 0; i < (g.wide ? 16 : 8); i++) {
101 uint32_t slice = g.data[j / (g.wide ? 2 : 4)];
102 uint32_t bit = 31 - ((j % (g.wide ? 2 : 4)) * (g.wide ? 16 : 8) + i);
103 uint32_t value = (slice >> bit) & 1;
104 if(value) {
105 uint32_t basex = rlist->x + rlist->scale * i;
106 uint32_t basey = rlist->y + rlist->scale * j;
107 for(uint32_t j2 = 0; j2 < rlist->scale; j2++)
108 for(uint32_t i2 = 0; i2 < rlist->scale; i2++)
109 target[(basey + j2) * 512 + (basex + i2)] = 0x7FFFF;
113 rlist++;
117 void draw_corrupt(uint32_t* target)
119 for(unsigned i = 0; i < 512 * 448; i++)
120 target[i] = 0x7FC00;
121 draw_special_screen(target, rl_corrupt);
124 function_ptr_command<arg_filename> take_screenshot_cmd(lsnes_cmd, "take-screenshot", "Takes a screenshot",
125 "Syntax: take-screenshot <file>\nSaves screenshot to PNG file <file>\n",
126 [](arg_filename file) throw(std::bad_alloc, std::runtime_error) {
127 take_screenshot(file);
128 messages << "Saved PNG screenshot" << std::endl;
131 bool last_redraw_no_lua = true;
134 framebuffer<false> main_screen;
136 void take_screenshot(const std::string& file) throw(std::bad_alloc, std::runtime_error)
138 render_info& ri = get_read_buffer();
139 ri.fbuf.save_png(file);
140 buffering.end_read();
144 void init_special_screens() throw(std::bad_alloc)
146 std::vector<uint32_t> buf;
147 buf.resize(512*448);
149 framebuffer_info inf;
150 inf.type = &_pixel_format_lrgb;
151 inf.mem = reinterpret_cast<char*>(&buf[0]);
152 inf.physwidth = 512;
153 inf.physheight = 448;
154 inf.physstride = 2048;
155 inf.width = 512;
156 inf.height = 448;
157 inf.stride = 2048;
158 inf.offset_x = 0;
159 inf.offset_y = 0;
161 draw_corrupt(&buf[0]);
162 screen_corrupt = framebuffer_raw(inf);
165 void redraw_framebuffer(framebuffer_raw& todraw, bool no_lua, bool spontaneous)
167 uint32_t hscl, vscl;
168 auto g = our_rom.rtype->get_scale_factors(todraw.get_width(), todraw.get_height());
169 hscl = g.first;
170 vscl = g.second;
171 render_info& ri = get_write_buffer();
172 ri.rq.clear();
173 struct lua_render_context lrc;
174 lrc.left_gap = 0;
175 lrc.right_gap = 0;
176 lrc.bottom_gap = 0;
177 lrc.top_gap = 0;
178 lrc.queue = &ri.rq;
179 lrc.width = todraw.get_width() * hscl;
180 lrc.height = todraw.get_height() * vscl;
181 if(!no_lua) {
182 lua_callback_do_paint(&lrc, spontaneous);
183 render_subtitles(lrc);
185 ri.fbuf = todraw;
186 ri.hscl = hscl;
187 ri.vscl = vscl;
188 ri.lgap = lrc.left_gap;
189 ri.rgap = lrc.right_gap;
190 ri.tgap = lrc.top_gap;
191 ri.bgap = lrc.bottom_gap;
192 buffering.end_write();
193 notify_screen_update();
194 last_redraw_no_lua = no_lua;
197 void redraw_framebuffer()
199 render_info& ri = get_read_buffer();
200 framebuffer_raw copy = ri.fbuf;
201 buffering.end_read();
202 //Redraws are never spontaneous
203 redraw_framebuffer(copy, last_redraw_no_lua, false);
207 void render_framebuffer()
209 render_info& ri = get_read_buffer();
210 main_screen.reallocate(ri.fbuf.get_width() * ri.hscl + ri.lgap + ri.rgap, ri.fbuf.get_height() * ri.vscl +
211 ri.tgap + ri.bgap);
212 main_screen.set_origin(ri.lgap, ri.tgap);
213 main_screen.copy_from(ri.fbuf, ri.hscl, ri.vscl);
214 ri.rq.run(main_screen);
215 notify_set_screen(main_screen);
216 //We would want divide by 2, but we'll do it ourselves in order to do mouse.
217 keyboard_key* mouse_x = lsnes_kbd.try_lookup_key("mouse_x");
218 keyboard_key* mouse_y = lsnes_kbd.try_lookup_key("mouse_y");
219 keyboard_mouse_calibration xcal;
220 keyboard_mouse_calibration ycal;
221 xcal.offset = ri.lgap;
222 ycal.offset = ri.tgap;
223 if(mouse_x && mouse_x->get_type() == KBD_KEYTYPE_MOUSE)
224 mouse_x->cast_mouse()->set_calibration(xcal);
225 if(mouse_y && mouse_y->get_type() == KBD_KEYTYPE_MOUSE)
226 mouse_y->cast_mouse()->set_calibration(ycal);
227 buffering.end_read();
230 std::pair<uint32_t, uint32_t> get_framebuffer_size()
232 uint32_t v, h;
233 render_info& ri = get_read_buffer();
234 v = ri.fbuf.get_width();
235 h = ri.fbuf.get_height();
236 buffering.end_read();
237 return std::make_pair(h, v);
240 framebuffer_raw get_framebuffer() throw(std::bad_alloc)
242 render_info& ri = get_read_buffer();
243 framebuffer_raw copy = ri.fbuf;
244 buffering.end_read();
245 return copy;
249 triplebuffer_logic::triplebuffer_logic() throw(std::bad_alloc)
251 last_complete_slot = 0;
252 read_active = false;
253 write_active = false;
254 read_active_slot = 0;
255 write_active_slot = 0;
258 triplebuffer_logic::~triplebuffer_logic() throw()
262 unsigned triplebuffer_logic::start_write() throw()
264 umutex_class h(mut);
265 if(!write_active) {
266 //We need to avoid hitting last complete slot or slot that is active for read.
267 if(last_complete_slot != 0 && read_active_slot != 0)
268 write_active_slot = 0;
269 else if(last_complete_slot != 1 && read_active_slot != 1)
270 write_active_slot = 1;
271 else
272 write_active_slot = 2;
274 write_active++;
275 return write_active_slot;
278 void triplebuffer_logic::end_write() throw()
280 umutex_class h(mut);
281 if(!--write_active)
282 last_complete_slot = write_active_slot;
285 unsigned triplebuffer_logic::start_read() throw()
287 umutex_class h(mut);
288 if(!read_active)
289 read_active_slot = last_complete_slot;
290 read_active++;
291 return read_active_slot;
294 void triplebuffer_logic::end_read() throw()
296 umutex_class h(mut);
297 read_active--;
300 void render_kill_request(void* obj)
302 buffer1.rq.kill_request(obj);
303 buffer2.rq.kill_request(obj);
304 buffer3.rq.kill_request(obj);
307 framebuffer_raw& render_get_latest_screen()
309 return get_read_buffer().fbuf;
312 void render_get_latest_screen_end()
314 buffering.end_read();