Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / framebuffer.cpp
blobf520cab2564a886d140f612909ebf8c0e7e27254
1 #include "framebuffer.hpp"
2 #include "hex.hpp"
3 #include "png.hpp"
4 #include "serialization.hpp"
5 #include "string.hpp"
6 #include "minmax.hpp"
7 #include "utf8.hpp"
8 #include <cstring>
9 #include <iostream>
10 #include <list>
12 #define TABSTOPS 64
13 #define SCREENSHOT_RGB_MAGIC 0x74212536U
15 namespace framebuffer
17 unsigned default_shift_r;
18 unsigned default_shift_g;
19 unsigned default_shift_b;
21 namespace
23 void recalculate_default_shifts()
25 uint32_t magic = 0x18000810;
26 default_shift_r = reinterpret_cast<uint8_t*>(&magic)[0];
27 default_shift_g = reinterpret_cast<uint8_t*>(&magic)[1];
28 default_shift_b = reinterpret_cast<uint8_t*>(&magic)[2];
31 std::list<pixfmt*>& pixfmts()
33 static std::list<pixfmt*> x;
34 return x;
37 template<size_t c> void decode_words(uint8_t* target, const uint8_t* src, size_t srcsize)
39 if(c == 1 || c == 2 || c == 3 || c == 4)
40 srcsize /= c;
41 else
42 srcsize /= 3;
43 if(c == 1) {
44 for(size_t i = 0; i < srcsize; i++)
45 target[i] = src[i];
46 } else if(c == 2) {
47 uint16_t* _target = reinterpret_cast<uint16_t*>(target);
48 for(size_t i = 0; i < srcsize; i++) {
49 _target[i] = static_cast<uint16_t>(src[2 * i + 0]) << 8;
50 _target[i] |= static_cast<uint16_t>(src[2 * i + 1]);
52 } else if(c == 3) {
53 for(size_t i = 0; i < srcsize; i++) {
54 target[3 * i + 0] = src[3 * i + 0];
55 target[3 * i + 1] = src[3 * i + 1];
56 target[3 * i + 2] = src[3 * i + 2];
58 } else if(c == 4) {
59 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
60 for(size_t i = 0; i < srcsize; i++) {
61 _target[i] = static_cast<uint32_t>(src[4 * i + 0]) << 24;
62 _target[i] |= static_cast<uint32_t>(src[4 * i + 1]) << 16;
63 _target[i] |= static_cast<uint32_t>(src[4 * i + 2]) << 8;
64 _target[i] |= static_cast<uint32_t>(src[4 * i + 3]);
66 } else if(c == 5) {
67 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
68 for(size_t i = 0; i < srcsize; i++) {
69 _target[i] = (static_cast<uint32_t>(src[3 * i + 0]) << 16);
70 _target[i] |= (static_cast<uint32_t>(src[3 * i + 1]) << 8);
71 _target[i] |= (static_cast<uint32_t>(src[3 * i + 2]));
76 template<size_t c> void encode_words(uint8_t* target, const uint8_t* src, size_t elts)
78 if(c == 1)
79 for(size_t i = 0; i < elts; i++)
80 target[i] = src[i];
81 else if(c == 2) {
82 const uint16_t* _src = reinterpret_cast<const uint16_t*>(src);
83 for(size_t i = 0; i < elts; i++) {
84 target[2 * i + 0] = _src[i] >> 8;
85 target[2 * i + 1] = _src[i];
87 } else if(c == 3) {
88 for(size_t i = 0; i < elts; i++) {
89 target[3 * i + 0] = src[3 * i + 0];
90 target[3 * i + 1] = src[3 * i + 1];
91 target[3 * i + 2] = src[3 * i + 2];
93 } else if(c == 4) {
94 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
95 for(size_t i = 0; i < elts; i++) {
96 target[4 * i + 0] = _src[i] >> 24;
97 target[4 * i + 1] = _src[i] >> 16;
98 target[4 * i + 2] = _src[i] >> 8;
99 target[4 * i + 3] = _src[i];
101 } else if(c == 5) {
102 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
103 for(size_t i = 0; i < elts; i++) {
104 target[3 * i + 0] = _src[i] >> 16;
105 target[3 * i + 1] = _src[i] >> 8;
106 target[3 * i + 2] = _src[i];
111 struct color_modifier
113 const char* name;
114 std::function<void(int64_t& v)> fn;
115 bool modifier;
118 std::map<std::string, std::pair<std::function<void(int64_t& v)>, bool>>& colornames()
120 static std::map<std::string, std::pair<std::function<void(int64_t& v)>, bool>> c;
121 return c;
125 basecolor::basecolor(const std::string& name, int64_t value)
127 colornames()[name] = std::make_pair([value](int64_t& v) { v = value; }, false);
130 color_mod::color_mod(const std::string& name, std::function<void(int64_t&)> fn)
132 colornames()[name] = std::make_pair(fn, true);
135 pixfmt::pixfmt() throw(std::bad_alloc)
137 pixfmts().push_back(this);
140 pixfmt::~pixfmt() throw()
142 for(auto i = pixfmts().begin(); i != pixfmts().end(); i++)
143 if(*i == this) {
144 pixfmts().erase(i);
145 break;
149 raw::raw(const info& info) throw(std::bad_alloc)
151 size_t unit = info.type->get_bpp();
152 size_t pixel_offset = info.offset_y * info.physstride + unit * info.offset_x;
153 user_memory = false;
154 addr = info.mem + pixel_offset;
155 fmt = info.type;
156 width = info.width;
157 height = info.height;
158 stride = info.stride;
159 allocated = 0;
162 raw::raw() throw(std::bad_alloc)
164 user_memory = true;
165 fmt = NULL;
166 addr = NULL;
167 width = 0;
168 height = 0;
169 stride = 0;
170 allocated = 0;
173 raw::raw(const raw& f) throw(std::bad_alloc)
175 user_memory = true;
176 fmt = f.fmt;
177 width = f.width;
178 height = f.height;
179 size_t unit = f.fmt->get_bpp();
180 stride = f.width * unit;
181 allocated = unit * width * height;
182 addr = new char[allocated];
183 for(size_t i = 0; i < height; i++)
184 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
187 raw& raw::operator=(const raw& f) throw(std::bad_alloc, std::runtime_error)
189 if(!user_memory)
190 throw std::runtime_error("Target framebuffer is not writable");
191 if(this == &f)
192 return *this;
193 size_t unit = f.fmt->get_bpp();
194 size_t newallocated = unit * f.width * f.height;
195 if(newallocated > allocated) {
196 char* newaddr = new char[newallocated];
197 delete[] addr;
198 addr = newaddr;
199 allocated = newallocated;
201 fmt = f.fmt;
202 width = f.width;
203 height = f.height;
204 stride = f.width * unit;
205 for(size_t i = 0; i < height; i++)
206 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
207 return *this;
210 raw::~raw()
212 if(user_memory)
213 delete[] addr;
216 void raw::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
218 if(data.size() < 2)
219 throw std::runtime_error("Bad screenshot data");
220 if(!user_memory)
221 throw std::runtime_error("Target framebuffer is not writable");
222 pixfmt* nfmt = NULL;
223 const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
224 size_t legacy_width = serialization::u16b(data2);
225 size_t dataoffset;
226 size_t _width;
227 size_t _height;
229 if(legacy_width > 0 && data.size() % (3 * legacy_width) == 2) {
230 //Legacy screenshot.
231 for(pixfmt* f : pixfmts())
232 if(f->get_magic() == 0)
233 nfmt = f;
234 if(!nfmt)
235 throw std::runtime_error("Unknown screenshot format");
236 _width = legacy_width;
237 _height = (data.size() - 2) / (3 * legacy_width);
238 dataoffset = 2;
239 } else {
240 //New format.
241 if(data.size() < 8)
242 throw std::runtime_error("Bad screenshot data");
243 dataoffset = 8;
244 uint32_t magic = serialization::u32b(data2 + 2);
245 for(pixfmt* f : pixfmts())
246 if(f->get_magic() == magic)
247 nfmt = f;
248 if(!nfmt)
249 throw std::runtime_error("Unknown screenshot format");
250 _width = serialization::u16b(data2 + 6);
251 _height = (data.size() - 8) / (nfmt->get_ss_bpp() * _width);
253 if(data.size() < dataoffset + nfmt->get_ss_bpp() * _width * _height)
254 throw std::runtime_error("Bad screenshot data");
256 size_t bpp = nfmt->get_bpp();
257 size_t sbpp = nfmt->get_ss_bpp();
258 if(allocated < bpp * _width * _height) {
259 //Allocate more memory.
260 size_t newalloc = bpp * _width * _height;
261 char* addr2 = new char[newalloc];
262 delete[] addr;
263 addr = addr2;
264 allocated = newalloc;
266 fmt = nfmt;
267 width = _width;
268 height = _height;
269 stride = _width * bpp;
270 if(bpp == 1)
271 decode_words<1>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
272 else if(bpp == 2)
273 decode_words<2>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
274 else if(bpp == 3)
275 decode_words<3>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
276 else if(bpp == 4 && sbpp == 3)
277 decode_words<5>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
278 else if(bpp == 4 && sbpp == 4)
279 decode_words<4>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
282 void raw::save(std::vector<char>& data) throw(std::bad_alloc)
284 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
285 unsigned m;
286 size_t bpp = fmt->get_bpp();
287 size_t sbpp = fmt->get_ss_bpp();
288 size_t offset;
289 uint8_t* data2;
290 uint32_t magic = fmt->get_magic();
291 switch(magic) {
292 case 0:
293 //Save in legacy format.
294 offset = 2;
295 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
296 data2 = reinterpret_cast<uint8_t*>(&data[0]);
297 serialization::u16b(&data[0], width);
298 break;
299 default:
300 //Choose the first two bytes so that screenshot is bad in legacy format.
301 m = 2;
302 while((sbpp * width * height + 8) % (3 * m) == 2)
303 m++;
304 offset = 8;
305 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
306 serialization::u16b(&data[0], m);
307 serialization::u32b(&data[2], magic);
308 serialization::u16b(&data[6], width);
309 break;
311 data2 = reinterpret_cast<uint8_t*>(&data[0]);
312 for(size_t i = 0; i < height; i++) {
313 if(bpp == 1)
314 encode_words<1>(data2 + offset + sbpp * width * i, memory + stride * i, width);
315 else if(bpp == 2)
316 encode_words<2>(data2 + offset + sbpp * width * i, memory + stride * i, width);
317 else if(bpp == 3)
318 encode_words<3>(data2 + offset + sbpp * width * i, memory + stride * i, width);
319 else if(bpp == 4 && sbpp == 3)
320 encode_words<5>(data2 + offset + sbpp * width * i, memory + stride * i, width);
321 else if(bpp == 4 && sbpp == 4)
322 encode_words<4>(data2 + offset + sbpp * width * i, memory + stride * i, width);
326 void raw::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
328 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
329 png::encoder img;
330 img.width = width;
331 img.height = height;
332 img.has_palette = false;
333 img.has_alpha = false;
334 img.data.resize(static_cast<size_t>(width) * height);
335 for(size_t i = 0; i < height; i++)
336 fmt->decode(&img.data[width * i], memory + stride * i, width);
337 try {
338 img.encode(file);
339 } catch(...) {
340 throw;
344 size_t raw::get_stride() const throw() { return stride; }
345 unsigned char* raw::get_start() const throw() { return reinterpret_cast<uint8_t*>(addr); }
346 pixfmt* raw::get_format() const throw() { return fmt; }
349 template<bool X>
350 fb<X>::fb() throw()
352 width = 0;
353 height = 0;
354 last_blit_w = 0;
355 last_blit_h = 0;
356 stride = 0;
357 offset_x = 0;
358 offset_y = 0;
359 mem = NULL;
360 user_mem = false;
361 upside_down = false;
362 current_fmt = NULL;
363 recalculate_default_shifts();
364 active_rshift = default_shift_r << (X ? 1 : 0);
365 active_gshift = default_shift_g << (X ? 1 : 0);
366 active_bshift = default_shift_b << (X ? 1 : 0);
369 template<bool X>
370 fb<X>::~fb() throw()
372 if(user_mem)
373 delete[] mem;
376 #define DECBUF_SIZE 4096
378 template<bool X>
379 void fb<X>::copy_from(raw& scr, size_t hscale, size_t vscale) throw()
381 typename fb<X>::element_t decbuf[DECBUF_SIZE];
382 last_blit_w = scr.width * hscale;
383 last_blit_h = scr.height * vscale;
385 if(!scr.fmt) {
386 for(size_t y = 0; y < height; y++)
387 memset(rowptr(y), 0, sizeof(typename fb<X>::element_t) * width);
388 return;
390 if(scr.fmt != current_fmt || active_rshift != auxpal.rshift || active_gshift != auxpal.gshift ||
391 active_bshift != auxpal.bshift) {
392 scr.fmt->set_palette(auxpal, active_rshift, active_gshift, active_bshift);
393 current_fmt = scr.fmt;
396 for(size_t y = 0; y < height; y++)
397 memset(rowptr(y), 0, sizeof(typename fb<X>::element_t) * width);
398 if(width < offset_x || height < offset_y) {
399 //Just clear the screen.
400 return;
402 size_t copyable_width = 0, copyable_height = 0;
403 if(hscale)
404 copyable_width = (width - offset_x) / hscale;
405 if(vscale)
406 copyable_height = (height - offset_y) / vscale;
407 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
408 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
410 for(size_t y = 0; y < copyable_height; y++) {
411 size_t line = y * vscale + offset_y;
412 const uint8_t* sbase = reinterpret_cast<uint8_t*>(scr.addr) + y * scr.stride;
413 typename fb<X>::element_t* ptr = rowptr(line) + offset_x;
414 size_t bpp = scr.fmt->get_bpp();
415 size_t xptr = 0;
416 size_t old_copyable_width = copyable_width;
417 while(copyable_width > DECBUF_SIZE) {
418 scr.fmt->decode(decbuf, sbase + xptr * bpp, DECBUF_SIZE, auxpal);
419 for(size_t k = 0; k < DECBUF_SIZE; k++)
420 for(size_t i = 0; i < hscale; i++)
421 *(ptr++) = decbuf[k];
422 xptr += DECBUF_SIZE;
423 copyable_width -= DECBUF_SIZE;
425 scr.fmt->decode(decbuf, sbase + xptr * bpp, copyable_width, auxpal);
426 for(size_t k = 0; k < copyable_width; k++)
427 for(size_t i = 0; i < hscale; i++)
428 *(ptr++) = decbuf[k];
429 copyable_width = old_copyable_width;
430 for(size_t j = 1; j < vscale; j++)
431 memcpy(rowptr(line + j) + offset_x, rowptr(line) + offset_x,
432 sizeof(typename fb<X>::element_t) * hscale * copyable_width);
436 template<bool X>
437 void fb<X>::set_palette(uint32_t r, uint32_t g, uint32_t b) throw(std::bad_alloc)
439 typename fb<X>::element_t R, G, B;
440 if(r == active_rshift && g == active_gshift && b == active_bshift)
441 return;
442 for(size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
443 typename fb<X>::element_t word = mem[i];
444 R = (word >> active_rshift) & (X ? 0xFFFF : 0xFF);
445 G = (word >> active_gshift) & (X ? 0xFFFF : 0xFF);
446 B = (word >> active_bshift) & (X ? 0xFFFF : 0xFF);
447 mem[i] = (R << r) | (G << g) | (B << b);
449 active_rshift = r;
450 active_gshift = g;
451 active_bshift = b;
454 template<bool X>
455 void fb<X>::set(element_t* _memory, size_t _width, size_t _height, size_t _pitch) throw()
457 if(user_mem && mem)
458 delete[] mem;
459 mem = _memory;
460 width = _width;
461 height = _height;
462 stride = _pitch;
463 user_mem = false;
464 upside_down = false;
467 template<bool X>
468 void fb<X>::reallocate(size_t _width, size_t _height, bool _upside_down) throw(std::bad_alloc)
470 size_t ustride = (_width + 11) / 12 * 12;
471 if(width != _width || height != _height) {
472 if(user_mem) {
473 element_t* newmem = new element_t[ustride * _height + 4];
474 delete[] mem;
475 mem = newmem;
476 } else
477 mem = new element_t[ustride * _height + 4];
479 memset(mem, 0, sizeof(element_t) * ustride * _height);
480 width = _width;
481 height = _height;
482 stride = ustride;
483 upside_down = _upside_down;
484 user_mem = true;
487 template<bool X>
488 void fb<X>::set_origin(size_t _offset_x, size_t _offset_y) throw()
490 offset_x = _offset_x;
491 offset_y = _offset_y;
494 template<bool X>
495 typename fb<X>::element_t* fb<X>::rowptr(size_t row) throw()
497 if(upside_down)
498 row = height - row - 1;
499 uint32_t align = (16 - reinterpret_cast<size_t>(mem)) % 16 / 4;
500 return mem + stride * row + align;
503 template<bool X>
504 const typename fb<X>::element_t* fb<X>::rowptr(size_t row) const throw()
506 if(upside_down)
507 row = height - row - 1;
508 return mem + stride * row;
511 template<bool X> uint8_t fb<X>::get_palette_r() const throw() { return auxpal.rshift; }
512 template<bool X> uint8_t fb<X>::get_palette_g() const throw() { return auxpal.gshift; }
513 template<bool X> uint8_t fb<X>::get_palette_b() const throw() { return auxpal.bshift; }
515 size_t raw::get_width() const throw() { return width; }
516 size_t raw::get_height() const throw() { return height; }
517 template<bool X> size_t fb<X>::get_origin_x() const throw() { return offset_x; }
518 template<bool X> size_t fb<X>::get_origin_y() const throw() { return offset_y; }
520 void queue::add(struct object& obj) throw(std::bad_alloc)
522 struct node* n = reinterpret_cast<struct node*>(alloc(sizeof(node)));
523 n->obj = &obj;
524 n->next = NULL;
525 n->killed = false;
526 if(queue_tail)
527 queue_tail = queue_tail->next = n;
528 else
529 queue_head = queue_tail = n;
532 void queue::copy_from(queue& q) throw(std::bad_alloc)
534 struct node* tmp = q.queue_head;
535 while(tmp) {
536 try {
537 tmp->obj->clone(*this);
538 tmp = tmp->next;
539 } catch(...) {
544 template<bool X> void queue::run(struct fb<X>& scr) throw()
546 struct node* tmp = queue_head;
547 while(tmp) {
548 try {
549 if(!tmp->killed)
550 (*(tmp->obj))(scr);
551 tmp = tmp->next;
552 } catch(...) {
557 void queue::clear() throw()
559 while(queue_head) {
560 if(!queue_head->killed)
561 queue_head->obj->~object();
562 queue_head = queue_head->next;
564 //Release all memory for reuse.
565 memory_allocated = 0;
566 pages = 0;
567 queue_tail = NULL;
570 void* queue::alloc(size_t block) throw(std::bad_alloc)
572 block = (block + 15) / 16 * 16;
573 if(block > RENDER_PAGE_SIZE)
574 throw std::bad_alloc();
575 if(pages == 0 || memory_allocated + block > pages * RENDER_PAGE_SIZE) {
576 memory_allocated = pages * RENDER_PAGE_SIZE;
577 memory[pages++];
579 void* mem = memory[memory_allocated / RENDER_PAGE_SIZE].content + (memory_allocated % RENDER_PAGE_SIZE);
580 memory_allocated += block;
581 return mem;
584 void queue::kill_request(void* obj) throw()
586 struct node* tmp = queue_head;
587 while(tmp) {
588 try {
589 if(!tmp->killed && tmp->obj->kill_request(obj)) {
590 //Kill this request.
591 tmp->killed = true;
592 tmp->obj->~object();
594 tmp = tmp->next;
595 } catch(...) {
600 queue::queue() throw()
602 queue_head = NULL;
603 queue_tail = NULL;
604 memory_allocated = 0;
605 pages = 0;
608 queue::~queue() throw()
610 clear();
613 object::object() throw()
617 object::~object() throw()
621 bool object::kill_request_ifeq(void* myobj, void* killobj)
623 if(!killobj)
624 return false;
625 if(myobj == killobj)
626 return true;
627 return false;
630 bool object::kill_request(void* obj) throw()
632 return false;
635 font::font() throw(std::bad_alloc)
637 bad_glyph_data[0] = 0x018001AAU;
638 bad_glyph_data[1] = 0x01800180U;
639 bad_glyph_data[2] = 0x01800180U;
640 bad_glyph_data[3] = 0x55800180U;
641 bad_glyph.wide = false;
642 bad_glyph.data = bad_glyph_data;
645 void font::load_hex_glyph(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
647 char buf2[8];
648 std::string line(data, data + size);
649 size_t linelen = line.length();
651 //Check if the line is valid.
652 //Line format is <hex digits>:<32 or 64 hex digits>.
653 size_t splitter = line.find(':');
654 if(splitter >= linelen || !(splitter + 33 == linelen || splitter + 65 == linelen))
655 (stringfmt() << "Invalid line '" << line << "'").throwex(); //No :, or not 32/64 hexes after.
656 if(line.find_first_not_of("0123456789ABCDEFabcdef:") < line.length())
657 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Invalid character.
658 if(line.find(':', splitter + 1) < line.length())
659 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Second :.
660 if(splitter > 7)
661 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Too long codepoint.
663 std::string codepoint = line.substr(0, splitter);
664 std::string cdata = line.substr(splitter + 1);
665 strcpy(buf2, codepoint.c_str());
666 char* end2;
667 unsigned long cp = strtoul(buf2, &end2, 16);
668 if(*end2 || cp > 0x10FFFF)
669 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Codepoint out of range.
670 glyphs[cp].wide = (cdata.length() == 64);
671 size_t p = memory.size();
672 for(size_t i = 0; i < cdata.length(); i += 8) {
673 char buf[9] = {0};
674 char* end;
675 for(size_t j = 0; j < 8; j++)
676 buf[j] = cdata[i + j];
677 memory.push_back(strtoul(buf, &end, 16));
679 glyphs[cp].offset = p;
682 void font::load_hex(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
684 const char* enddata = data + size;
685 while(data != enddata) {
686 size_t linesize = 0;
687 while(data + linesize != enddata && data[linesize] != '\n' && data[linesize] != '\r')
688 linesize++;
689 if(linesize && data[0] != '#')
690 load_hex_glyph(data, linesize);
691 data += linesize;
692 if(data != enddata)
693 data++;
695 memory.push_back(0);
696 memory.push_back(0);
697 memory.push_back(0);
698 memory.push_back(0);
699 glyphs[32].wide = false;
700 glyphs[32].offset = memory.size() - 4;
701 for(auto& i : glyphs)
702 i.second.data = &memory[i.second.offset];
705 const font::glyph& font::get_glyph(uint32_t glyph) throw()
707 if(glyphs.count(glyph))
708 return glyphs[glyph];
709 else
710 return bad_glyph;
713 std::set<uint32_t> font::get_glyphs_set()
715 std::set<uint32_t> out;
716 for(auto& i : glyphs)
717 out.insert(i.first);
718 return out;
721 const font::glyph& font::get_bad_glyph() throw()
723 return bad_glyph;
726 std::pair<size_t, size_t> font::get_metrics(const std::string& string) throw()
728 size_t commit_width = 0;
729 size_t commit_height = 0;
730 size_t linelength = 0;
731 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([this, &linelength, &commit_width,
732 &commit_height](const int32_t& cp) -> void {
733 const glyph& g = get_glyph(cp);
734 switch(cp) {
735 case 9:
736 linelength = (linelength + TABSTOPS) / TABSTOPS * TABSTOPS;
737 commit_width = max(commit_width, linelength);
738 break;
739 case 10:
740 commit_height += 16;
741 break;
742 default:
743 linelength = linelength + (g.wide ? 16 : 8);
744 commit_width = max(commit_width, linelength);
745 break;
747 }));
748 return std::make_pair(commit_width, commit_height);
751 std::vector<font::layout> font::dolayout(const std::string& string) throw(std::bad_alloc)
753 //First, calculate the number of glyphs to draw.
754 size_t chars = 0;
755 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([&chars](const int32_t& cp)
756 -> void {
757 if(cp != 9 && cp != 10)
758 chars++;
759 }));
760 //Allocate space.
761 std::vector<layout> l;
762 l.resize(chars);
763 size_t gtr = 0;
764 size_t layout_x = 0;
765 size_t layout_y = 0;
766 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([this, &layout_x, &layout_y,
767 &l, &gtr](const int32_t cp) {
768 const glyph& g = get_glyph(cp);
769 switch(cp) {
770 case 9:
771 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
772 break;
773 case 10:
774 layout_x = 0;
775 layout_y = layout_y + 16;
776 break;
777 default:
778 l[gtr].x = layout_x;
779 l[gtr].y = layout_y;
780 l[gtr++].dglyph = &g;
781 layout_x = layout_x + (g.wide ? 16 : 8);;
783 }));
784 return l;
787 template<bool X> void font::render(struct fb<X>& scr, int32_t x, int32_t y, const std::string& text,
788 color fg, color bg, bool hdbl, bool vdbl) throw()
790 x += scr.get_origin_x();
791 y += scr.get_origin_y();
792 size_t layout_x = 0;
793 size_t layout_y = 0;
794 size_t swidth = scr.get_width();
795 size_t sheight = scr.get_height();
796 utf8::to32i(text.begin(), text.end(), lambda_output_iterator<int32_t>([this, x, y, &scr, &layout_x,
797 &layout_y, swidth, sheight, hdbl, vdbl, &fg, &bg](const int32_t& cp) {
798 const glyph& g = get_glyph(cp);
799 switch(cp) {
800 case 9:
801 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
802 break;
803 case 10:
804 layout_x = 0;
805 layout_y = layout_y + (vdbl ? 32 : 16);
806 break;
807 default:
808 //Render this glyph at x + layout_x, y + layout_y.
809 int32_t gx = x + layout_x;
810 int32_t gy = y + layout_y;
811 //Don't draw characters completely off-screen.
812 if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight)
813 break;
814 if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth)
815 break;
816 //Compute the bounding box.
817 uint32_t xstart = 0;
818 uint32_t ystart = 0;
819 uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
820 uint32_t ylength = (vdbl ? 32 : 16);
821 if(gx < 0) xstart = -gx;
822 if(gy < 0) ystart = -gy;
823 xlength -= xstart;
824 ylength -= ystart;
825 if(gx + xstart + xlength > swidth) xlength = swidth - (gx + xstart);
826 if(gy + ystart + ylength > sheight) ylength = sheight - (gy + ystart);
827 if(g.data)
828 for(size_t i = 0; i < ylength; i++) {
829 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) +
830 (gx + xstart);
831 uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0);
832 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
833 if(g.wide)
834 d >>= 16 - ((_y & 1) << 4);
835 else
836 d >>= 24 - ((_y & 3) << 3);
837 if(hdbl)
838 for(size_t j = 0; j < xlength; j++) {
839 uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1);
840 if(((d >> b) & 1) != 0)
841 fg.apply(r[j]);
842 else
843 bg.apply(r[j]);
845 else
846 for(size_t j = 0; j < xlength; j++) {
847 uint32_t b = (g.wide ? 15 : 7) - (j + xstart);
848 if(((d >> b) & 1) != 0)
849 fg.apply(r[j]);
850 else
851 bg.apply(r[j]);
854 else
855 for(size_t i = 0; i < ylength; i++) {
856 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) +
857 (gx + xstart);
858 for(size_t j = 0; j < xlength; j++)
859 bg.apply(r[j]);
861 layout_x += (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
863 }));
866 color::color(const std::string& clr) throw(std::bad_alloc, std::runtime_error)
868 int64_t col = -1;
869 bool first = true;
870 auto& cspecs = colornames();
871 for(auto& t : token_iterator<char>::foreach(clr, {" ","\t"}, true)) {
872 if(t.length() > 0 && t[0] == '#') {
873 if(!first)
874 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
875 std::string _t = t;
876 col = hex::from<uint32_t>(_t.substr(1));
877 first = false;
878 continue;
880 if(!cspecs.count(t))
881 throw std::runtime_error("Invalid color (modifier) '" + t + "'");
882 if(!first && !cspecs[t].second)
883 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
884 if(first && cspecs[t].second)
885 throw std::runtime_error("Modifier (" + t + ") can't be used as base color");
886 (cspecs[t].first)(col);
887 first = false;
889 *this = color(col);
892 std::string color::stringify(int64_t number)
894 for(auto& i : colornames()) {
895 int64_t col = -1;
896 if(i.second.second)
897 continue;
898 (i.second.first)(col);
899 if(col == number)
900 return i.first;
902 if(number < 0)
903 return "transparent";
904 else if(number < 16777216)
905 return "#" + hex::to<uint32_t>(number).substr(2);
906 else
907 return "#" + hex::to<uint32_t>(number);
910 void color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift, bool X) throw()
912 if(X) {
913 uint64_t r = ((orig >> 16) & 0xFF) * 257;
914 uint64_t g = ((orig >> 8) & 0xFF) * 257;
915 uint64_t b = (orig & 0xFF) * 257;
916 uint64_t a = 65535;
917 uint64_t fullc = ~0ULL & ~((a << rshift) | (a << gshift) | (a << bshift));
918 uint64_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
919 hiHI = color & 0xFFFF0000FFFFULL;
920 loHI = (color & 0xFFFF0000FFFF0000ULL) >> 16;
921 hiHI *= (static_cast<uint32_t>(origa) * 256);
922 loHI *= (static_cast<uint32_t>(origa) * 256);
923 } else {
924 uint32_t r = (orig >> 16) & 0xFF;
925 uint32_t g = (orig >> 8) & 0xFF;
926 uint32_t b = orig & 0xFF;
927 uint32_t a = 255;
928 uint64_t fullc = ~0UL & ~((a << rshift) | (a << gshift) | (a << bshift));
929 uint32_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
930 hi = color & 0xFF00FF;
931 lo = (color & 0xFF00FF00) >> 8;
932 hi *= origa;
933 lo *= origa;
937 namespace
939 void adjust_hmM_hue(int16_t& hue, int16_t& m, int16_t& M, double adj)
941 if(m == M)
942 return;
943 int16_t S = M - m;
944 hue = (hue + static_cast<uint32_t>(adj * S)) % (6 * S);
946 void adjust_ls_saturation(double& s, double& l, double adj)
948 s = clip(s + adj, 0.0, 1.0);
950 void adjust_ls_lightness(double& s, double& l, double adj)
952 l = clip(l + adj, 0.0, 1.0);
954 template<void(*adjustfn)(double& s, double& l, double adj)>
955 void adjust_hmM_sl(int16_t& hue, int16_t& m, int16_t& M, double adj)
957 int16_t S1 = M - m;
958 double _m = m / 255.0;
959 double _M = M / 255.0;
960 double l = (_m + _M) / 2;
961 double s;
962 if(l == 0 || l == 1) s = 0;
963 else if(l <= 0.5) s = _M / l - 1;
964 else s = (_M - l) / (1 - l);
965 adjustfn(s, l, adj);
966 if(l <= 0.5) _M = l * (s + 1);
967 else _M = l + s - l * s;
968 _m = 2 * l -_M;
969 m = _m * 255;
970 M = _M * 255;
971 int32_t S2 = M - m;
972 hue = S1 ? (S2 * hue / S1) : 0;
974 //0: m
975 //1: M
976 //2: m + phue
977 //3: M - phue
978 const uint8_t hsl2rgb_flags[] = {24, 52, 6, 13, 33, 19};
979 template<void(*adjustfn)(int16_t& hue, int16_t& m, int16_t& M, double adj)>
980 uint32_t adjustcolor(uint32_t color, double shift)
982 int16_t R = (color >> 16) & 0xFF;
983 int16_t G = (color >> 8) & 0xFF;
984 int16_t B = color & 0xFF;
985 int16_t m = min(R, min(G, B));
986 int16_t M = max(R, max(G, B));
987 int16_t S1 = M - m;
988 int16_t hue;
989 if(R == M)
990 hue = G - B + 6 * S1;
991 else if(G == M)
992 hue = B - R + 2 * S1;
993 else
994 hue = R - G + 4 * S1;
995 adjustfn(hue, m, M, shift);
996 if(m == M)
997 return ((uint32_t)m << 16) | ((uint32_t)m << 8) | (uint32_t)m;
998 int16_t S2 = M - m;
999 hue %= (6 * S2);
1000 uint32_t V[4];
1001 V[0] = m;
1002 V[1] = M;
1003 V[2] = m + hue % S2;
1004 V[3] = M - hue % S2;
1005 uint8_t flag = hsl2rgb_flags[hue / S2];
1006 return (V[(flag >> 4) & 3] << 16) | (V[(flag >> 2) & 3] << 8) | (V[flag & 3]);
1010 int64_t color_rotate_hue(int64_t basecolor, int step, int steps)
1012 if(!steps)
1013 throw std::runtime_error("Expected nonzero steps for hue rotation");
1014 if(basecolor < 0) {
1015 //Special: Any rotation of transparent is transparent.
1016 return -1;
1018 uint32_t asteps = std::abs(steps);
1019 if(steps < 0)
1020 step = asteps - step % asteps; //Reverse order.
1021 double hueshift = 6.0 * (step % asteps) / asteps;
1022 basecolor = adjustcolor<adjust_hmM_hue>(basecolor & 0xFFFFFF, hueshift) | (basecolor & 0xFF000000);
1023 return basecolor;
1026 int64_t color_adjust_saturation(int64_t color, double adjust)
1028 if(color < 0) return color;
1029 return adjustcolor<adjust_hmM_sl<adjust_ls_saturation>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1032 int64_t color_adjust_lightness(int64_t color, double adjust)
1034 if(color < 0) return color;
1035 return adjustcolor<adjust_hmM_sl<adjust_ls_lightness>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1038 template class fb<false>;
1039 template class fb<true>;
1040 template void queue::run(struct fb<false>&);
1041 template void queue::run(struct fb<true>&);
1042 template void font::render(struct fb<false>& scr, int32_t x, int32_t y, const std::string& text,
1043 color fg, color bg, bool hdbl, bool vdbl) throw();
1044 template void font::render(struct fb<true>& scr, int32_t x, int32_t y, const std::string& text,
1045 color fg, color bg, bool hdbl, bool vdbl) throw();