Some tweaks to Lua docs
[lsnes.git] / src / library / framebuffer.cpp
blob7fb280f4a302002c7fc1fc58754647dba993af6a
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 clip_range(uint32_t origin, uint32_t size, int32_t base, int32_t& minc, int32_t& maxc) throw()
522 int64_t _origin = origin;
523 int64_t _size = size;
524 int64_t _base = base;
525 int64_t _minc = minc;
526 int64_t _maxc = maxc;
527 int64_t mincoordinate = _base + _origin + _minc;
528 int64_t maxcoordinate = _base + _origin + _maxc;
529 if(mincoordinate < 0)
530 _minc = _minc - mincoordinate;
531 if(maxcoordinate > _size)
532 _maxc = _maxc - (maxcoordinate - _size);
533 if(_minc >= maxc) {
534 minc = 0;
535 maxc = 0;
536 } else {
537 minc = _minc;
538 maxc = _maxc;
542 void queue::add(struct object& obj) throw(std::bad_alloc)
544 struct node* n = reinterpret_cast<struct node*>(alloc(sizeof(node)));
545 n->obj = &obj;
546 n->next = NULL;
547 n->killed = false;
548 if(queue_tail)
549 queue_tail = queue_tail->next = n;
550 else
551 queue_head = queue_tail = n;
554 void queue::copy_from(queue& q) throw(std::bad_alloc)
556 struct node* tmp = q.queue_head;
557 while(tmp) {
558 try {
559 tmp->obj->clone(*this);
560 tmp = tmp->next;
561 } catch(...) {
566 template<bool X> void queue::run(struct fb<X>& scr) throw()
568 struct node* tmp = queue_head;
569 while(tmp) {
570 try {
571 if(!tmp->killed)
572 (*(tmp->obj))(scr);
573 tmp = tmp->next;
574 } catch(...) {
579 void queue::clear() throw()
581 while(queue_head) {
582 if(!queue_head->killed)
583 queue_head->obj->~object();
584 queue_head = queue_head->next;
586 //Release all memory for reuse.
587 memory_allocated = 0;
588 pages = 0;
589 queue_tail = NULL;
592 void* queue::alloc(size_t block) throw(std::bad_alloc)
594 block = (block + 15) / 16 * 16;
595 if(block > RENDER_PAGE_SIZE)
596 throw std::bad_alloc();
597 if(pages == 0 || memory_allocated + block > pages * RENDER_PAGE_SIZE) {
598 memory_allocated = pages * RENDER_PAGE_SIZE;
599 memory[pages++];
601 void* mem = memory[memory_allocated / RENDER_PAGE_SIZE].content + (memory_allocated % RENDER_PAGE_SIZE);
602 memory_allocated += block;
603 return mem;
606 void queue::kill_request(void* obj) throw()
608 struct node* tmp = queue_head;
609 while(tmp) {
610 try {
611 if(!tmp->killed && tmp->obj->kill_request(obj)) {
612 //Kill this request.
613 tmp->killed = true;
614 tmp->obj->~object();
616 tmp = tmp->next;
617 } catch(...) {
622 queue::queue() throw()
624 queue_head = NULL;
625 queue_tail = NULL;
626 memory_allocated = 0;
627 pages = 0;
630 queue::~queue() throw()
632 clear();
635 object::object() throw()
639 object::~object() throw()
643 bool object::kill_request_ifeq(void* myobj, void* killobj)
645 if(!killobj)
646 return false;
647 if(myobj == killobj)
648 return true;
649 return false;
652 bool object::kill_request(void* obj) throw()
654 return false;
657 font::font() throw(std::bad_alloc)
659 bad_glyph_data[0] = 0x018001AAU;
660 bad_glyph_data[1] = 0x01800180U;
661 bad_glyph_data[2] = 0x01800180U;
662 bad_glyph_data[3] = 0x55800180U;
663 bad_glyph.wide = false;
664 bad_glyph.data = bad_glyph_data;
667 void font::load_hex_glyph(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
669 char buf2[8];
670 std::string line(data, data + size);
671 regex_results r;
672 if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{32})", line)) {
673 } else if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{64})", line)) {
674 } else
675 (stringfmt() << "Invalid line '" << line << "'").throwex();
676 std::string codepoint = r[1];
677 std::string cdata = r[2];
678 if(codepoint.length() > 7)
679 (stringfmt() << "Invalid line '" << line << "'").throwex();
680 strcpy(buf2, codepoint.c_str());
681 char* end2;
682 unsigned long cp = strtoul(buf2, &end2, 16);
683 if(*end2 || cp > 0x10FFFF)
684 (stringfmt() << "Invalid line '" << line << "'").throwex();
685 glyphs[cp].wide = (cdata.length() == 64);
686 size_t p = memory.size();
687 for(size_t i = 0; i < cdata.length(); i += 8) {
688 char buf[9] = {0};
689 char* end;
690 for(size_t j = 0; j < 8; j++)
691 buf[j] = cdata[i + j];
692 memory.push_back(strtoul(buf, &end, 16));
694 glyphs[cp].offset = p;
697 void font::load_hex(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
699 const char* enddata = data + size;
700 while(data != enddata) {
701 size_t linesize = 0;
702 while(data + linesize != enddata && data[linesize] != '\n' && data[linesize] != '\r')
703 linesize++;
704 if(linesize && data[0] != '#')
705 load_hex_glyph(data, linesize);
706 data += linesize;
707 if(data != enddata)
708 data++;
710 memory.push_back(0);
711 memory.push_back(0);
712 memory.push_back(0);
713 memory.push_back(0);
714 glyphs[32].wide = false;
715 glyphs[32].offset = memory.size() - 4;
716 for(auto& i : glyphs)
717 i.second.data = &memory[i.second.offset];
720 const font::glyph& font::get_glyph(uint32_t glyph) throw()
722 if(glyphs.count(glyph))
723 return glyphs[glyph];
724 else
725 return bad_glyph;
728 std::set<uint32_t> font::get_glyphs_set()
730 std::set<uint32_t> out;
731 for(auto& i : glyphs)
732 out.insert(i.first);
733 return out;
736 const font::glyph& font::get_bad_glyph() throw()
738 return bad_glyph;
741 std::pair<size_t, size_t> font::get_metrics(const std::string& string) throw()
743 size_t commit_width = 0;
744 size_t commit_height = 0;
745 size_t linelength = 0;
746 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([this, &linelength, &commit_width,
747 &commit_height](const int32_t& cp) -> void {
748 const glyph& g = get_glyph(cp);
749 switch(cp) {
750 case 9:
751 linelength = (linelength + TABSTOPS) / TABSTOPS * TABSTOPS;
752 commit_width = max(commit_width, linelength);
753 break;
754 case 10:
755 commit_height += 16;
756 break;
757 default:
758 linelength = linelength + (g.wide ? 16 : 8);
759 commit_width = max(commit_width, linelength);
760 break;
762 }));
763 return std::make_pair(commit_width, commit_height);
766 std::vector<font::layout> font::dolayout(const std::string& string) throw(std::bad_alloc)
768 //First, calculate the number of glyphs to draw.
769 size_t chars = 0;
770 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([&chars](const int32_t& cp)
771 -> void {
772 if(cp != 9 && cp != 10)
773 chars++;
774 }));
775 //Allocate space.
776 std::vector<layout> l;
777 l.resize(chars);
778 size_t gtr = 0;
779 size_t layout_x = 0;
780 size_t layout_y = 0;
781 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([this, &layout_x, &layout_y,
782 &l, &gtr](const int32_t cp) {
783 const glyph& g = get_glyph(cp);
784 switch(cp) {
785 case 9:
786 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
787 break;
788 case 10:
789 layout_x = 0;
790 layout_y = layout_y + 16;
791 break;
792 default:
793 l[gtr].x = layout_x;
794 l[gtr].y = layout_y;
795 l[gtr++].dglyph = &g;
796 layout_x = layout_x + (g.wide ? 16 : 8);;
798 }));
799 return l;
802 template<bool X> void font::render(struct fb<X>& scr, int32_t x, int32_t y, const std::string& text,
803 color fg, color bg, bool hdbl, bool vdbl) throw()
805 x += scr.get_origin_x();
806 y += scr.get_origin_y();
807 size_t layout_x = 0;
808 size_t layout_y = 0;
809 size_t swidth = scr.get_width();
810 size_t sheight = scr.get_height();
811 utf8::to32i(text.begin(), text.end(), lambda_output_iterator<int32_t>([this, x, y, &scr, &layout_x,
812 &layout_y, swidth, sheight, hdbl, vdbl, &fg, &bg](const int32_t& cp) {
813 const glyph& g = get_glyph(cp);
814 switch(cp) {
815 case 9:
816 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
817 break;
818 case 10:
819 layout_x = 0;
820 layout_y = layout_y + (vdbl ? 32 : 16);
821 break;
822 default:
823 //Render this glyph at x + layout_x, y + layout_y.
824 int32_t gx = x + layout_x;
825 int32_t gy = y + layout_y;
826 //Don't draw characters completely off-screen.
827 if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight)
828 break;
829 if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth)
830 break;
831 //Compute the bounding box.
832 uint32_t xstart = 0;
833 uint32_t ystart = 0;
834 uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
835 uint32_t ylength = (vdbl ? 32 : 16);
836 if(gx < 0) xstart = -gx;
837 if(gy < 0) ystart = -gy;
838 xlength -= xstart;
839 ylength -= ystart;
840 if(gx + xstart + xlength > swidth) xlength = swidth - (gx + xstart);
841 if(gy + ystart + ylength > sheight) ylength = sheight - (gy + ystart);
842 if(g.data)
843 for(size_t i = 0; i < ylength; i++) {
844 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) +
845 (gx + xstart);
846 uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0);
847 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
848 if(g.wide)
849 d >>= 16 - ((_y & 1) << 4);
850 else
851 d >>= 24 - ((_y & 3) << 3);
852 if(hdbl)
853 for(size_t j = 0; j < xlength; j++) {
854 uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1);
855 if(((d >> b) & 1) != 0)
856 fg.apply(r[j]);
857 else
858 bg.apply(r[j]);
860 else
861 for(size_t j = 0; j < xlength; j++) {
862 uint32_t b = (g.wide ? 15 : 7) - (j + xstart);
863 if(((d >> b) & 1) != 0)
864 fg.apply(r[j]);
865 else
866 bg.apply(r[j]);
869 else
870 for(size_t i = 0; i < ylength; i++) {
871 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) +
872 (gx + xstart);
873 for(size_t j = 0; j < xlength; j++)
874 bg.apply(r[j]);
876 layout_x += (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
878 }));
881 color::color(const std::string& clr) throw(std::bad_alloc, std::runtime_error)
883 int64_t col = -1;
884 bool first = true;
885 auto& cspecs = colornames();
886 for(auto& t : token_iterator_foreach(clr, {" ","\t"}, true)) {
887 if(t.length() > 0 && t[0] == '#') {
888 if(!first)
889 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
890 std::string _t = t;
891 col = hex::from<uint32_t>(_t.substr(1));
892 first = false;
893 continue;
895 if(!cspecs.count(t))
896 throw std::runtime_error("Invalid color (modifier) '" + t + "'");
897 if(!first && !cspecs[t].second)
898 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
899 if(first && cspecs[t].second)
900 throw std::runtime_error("Modifier (" + t + ") can't be used as base color");
901 (cspecs[t].first)(col);
902 first = false;
904 *this = color(col);
907 std::string color::stringify(int64_t number)
909 auto& cspecs = colornames();
910 for(auto& i : colornames()) {
911 int64_t col = -1;
912 if(i.second.second)
913 continue;
914 (i.second.first)(col);
915 if(col == number)
916 return i.first;
918 if(number < 0)
919 return "transparent";
920 else if(number < 16777216)
921 return "#" + hex::to<uint32_t>(number).substr(2);
922 else
923 return "#" + hex::to<uint32_t>(number);
926 void color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift, bool X) throw()
928 if(X) {
929 uint64_t r = ((orig >> 16) & 0xFF) * 257;
930 uint64_t g = ((orig >> 8) & 0xFF) * 257;
931 uint64_t b = (orig & 0xFF) * 257;
932 uint64_t a = 65535;
933 uint64_t fullc = ~0ULL & ~((a << rshift) | (a << gshift) | (a << bshift));
934 uint64_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
935 hiHI = color & 0xFFFF0000FFFFULL;
936 loHI = (color & 0xFFFF0000FFFF0000ULL) >> 16;
937 hiHI *= (static_cast<uint32_t>(origa) * 256);
938 loHI *= (static_cast<uint32_t>(origa) * 256);
939 } else {
940 uint32_t r = (orig >> 16) & 0xFF;
941 uint32_t g = (orig >> 8) & 0xFF;
942 uint32_t b = orig & 0xFF;
943 uint32_t a = 255;
944 uint64_t fullc = ~0UL & ~((a << rshift) | (a << gshift) | (a << bshift));
945 uint32_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
946 hi = color & 0xFF00FF;
947 lo = (color & 0xFF00FF00) >> 8;
948 hi *= origa;
949 lo *= origa;
953 namespace
955 void adjust_hmM_hue(int16_t& hue, int16_t& m, int16_t& M, double adj)
957 if(m == M)
958 return;
959 int16_t S = M - m;
960 hue = (hue + static_cast<uint32_t>(adj * S)) % (6 * S);
962 void adjust_ls_saturation(double& s, double& l, double adj)
964 s = clip(s + adj, 0.0, 1.0);
966 void adjust_ls_lightness(double& s, double& l, double adj)
968 l = clip(l + adj, 0.0, 1.0);
970 template<void(*adjustfn)(double& s, double& l, double adj)>
971 void adjust_hmM_sl(int16_t& hue, int16_t& m, int16_t& M, double adj)
973 int16_t S1 = M - m;
974 double _m = m / 255.0;
975 double _M = M / 255.0;
976 double l = (_m + _M) / 2;
977 double s;
978 if(l == 0 || l == 1) s = 0;
979 else if(l <= 0.5) s = _M / l - 1;
980 else s = (_M - l) / (1 - l);
981 adjustfn(s, l, adj);
982 if(l <= 0.5) _M = l * (s + 1);
983 else _M = l + s - l * s;
984 _m = 2 * l -_M;
985 m = _m * 255;
986 M = _M * 255;
987 int32_t S2 = M - m;
988 hue = S1 ? (S2 * hue / S1) : 0;
990 //0: m
991 //1: M
992 //2: m + phue
993 //3: M - phue
994 const uint8_t hsl2rgb_flags[] = {24, 52, 6, 13, 33, 19};
995 template<void(*adjustfn)(int16_t& hue, int16_t& m, int16_t& M, double adj)>
996 uint32_t adjustcolor(uint32_t color, double shift)
998 int16_t R = (color >> 16) & 0xFF;
999 int16_t G = (color >> 8) & 0xFF;
1000 int16_t B = color & 0xFF;
1001 int16_t m = min(R, min(G, B));
1002 int16_t M = max(R, max(G, B));
1003 int16_t S1 = M - m;
1004 int16_t hue;
1005 if(R == M)
1006 hue = G - B + 6 * S1;
1007 else if(G == M)
1008 hue = B - R + 2 * S1;
1009 else
1010 hue = R - G + 4 * S1;
1011 adjustfn(hue, m, M, shift);
1012 if(m == M)
1013 return ((uint32_t)m << 16) | ((uint32_t)m << 8) | (uint32_t)m;
1014 int16_t S2 = M - m;
1015 hue %= (6 * S2);
1016 uint32_t V[4];
1017 V[0] = m;
1018 V[1] = M;
1019 V[2] = m + hue % S2;
1020 V[3] = M - hue % S2;
1021 uint8_t flag = hsl2rgb_flags[hue / S2];
1022 return (V[(flag >> 4) & 3] << 16) | (V[(flag >> 2) & 3] << 8) | (V[flag & 3]);
1026 int64_t color_rotate_hue(int64_t basecolor, int step, int steps)
1028 if(!steps)
1029 throw std::runtime_error("Expected nonzero steps for hue rotation");
1030 if(basecolor < 0) {
1031 //Special: Any rotation of transparent is transparent.
1032 return -1;
1034 uint32_t asteps = std::abs(steps);
1035 if(steps < 0)
1036 step = asteps - step % asteps; //Reverse order.
1037 double hueshift = 6.0 * (step % asteps) / asteps;
1038 basecolor = adjustcolor<adjust_hmM_hue>(basecolor & 0xFFFFFF, hueshift) | (basecolor & 0xFF000000);
1039 return basecolor;
1042 int64_t color_adjust_saturation(int64_t color, double adjust)
1044 if(color < 0) return color;
1045 return adjustcolor<adjust_hmM_sl<adjust_ls_saturation>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1048 int64_t color_adjust_lightness(int64_t color, double adjust)
1050 if(color < 0) return color;
1051 return adjustcolor<adjust_hmM_sl<adjust_ls_lightness>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1054 template class fb<false>;
1055 template class fb<true>;
1056 template void queue::run(struct fb<false>&);
1057 template void queue::run(struct fb<true>&);
1058 template void font::render(struct fb<false>& scr, int32_t x, int32_t y, const std::string& text,
1059 color fg, color bg, bool hdbl, bool vdbl) throw();
1060 template void font::render(struct fb<true>& scr, int32_t x, int32_t y, const std::string& text,
1061 color fg, color bg, bool hdbl, bool vdbl) throw();