Small whitespace cleanup
[lsnes.git] / src / library / framebuffer.cpp
blobe40a308aa1b95a9d269e865d41cf4c28327bdd9d
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 const char* render_page_id = "Render queues";
18 unsigned default_shift_r;
19 unsigned default_shift_g;
20 unsigned default_shift_b;
22 namespace
24 void recalculate_default_shifts()
26 uint32_t magic = 0x18000810;
27 default_shift_r = reinterpret_cast<uint8_t*>(&magic)[0];
28 default_shift_g = reinterpret_cast<uint8_t*>(&magic)[1];
29 default_shift_b = reinterpret_cast<uint8_t*>(&magic)[2];
32 std::list<pixfmt*>& pixfmts()
34 static std::list<pixfmt*> x;
35 return x;
38 template<size_t c> void decode_words(uint8_t* target, const uint8_t* src, size_t srcsize)
40 if(c == 1 || c == 2 || c == 3 || c == 4)
41 srcsize /= c;
42 else
43 srcsize /= 3;
44 if(c == 1) {
45 for(size_t i = 0; i < srcsize; i++)
46 target[i] = src[i];
47 } else if(c == 2) {
48 uint16_t* _target = reinterpret_cast<uint16_t*>(target);
49 for(size_t i = 0; i < srcsize; i++) {
50 _target[i] = static_cast<uint16_t>(src[2 * i + 0]) << 8;
51 _target[i] |= static_cast<uint16_t>(src[2 * i + 1]);
53 } else if(c == 3) {
54 for(size_t i = 0; i < srcsize; i++) {
55 target[3 * i + 0] = src[3 * i + 0];
56 target[3 * i + 1] = src[3 * i + 1];
57 target[3 * i + 2] = src[3 * i + 2];
59 } else if(c == 4) {
60 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
61 for(size_t i = 0; i < srcsize; i++) {
62 _target[i] = static_cast<uint32_t>(src[4 * i + 0]) << 24;
63 _target[i] |= static_cast<uint32_t>(src[4 * i + 1]) << 16;
64 _target[i] |= static_cast<uint32_t>(src[4 * i + 2]) << 8;
65 _target[i] |= static_cast<uint32_t>(src[4 * i + 3]);
67 } else if(c == 5) {
68 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
69 for(size_t i = 0; i < srcsize; i++) {
70 _target[i] = (static_cast<uint32_t>(src[3 * i + 0]) << 16);
71 _target[i] |= (static_cast<uint32_t>(src[3 * i + 1]) << 8);
72 _target[i] |= (static_cast<uint32_t>(src[3 * i + 2]));
77 template<size_t c> void encode_words(uint8_t* target, const uint8_t* src, size_t elts)
79 if(c == 1)
80 for(size_t i = 0; i < elts; i++)
81 target[i] = src[i];
82 else if(c == 2) {
83 const uint16_t* _src = reinterpret_cast<const uint16_t*>(src);
84 for(size_t i = 0; i < elts; i++) {
85 target[2 * i + 0] = _src[i] >> 8;
86 target[2 * i + 1] = _src[i];
88 } else if(c == 3) {
89 for(size_t i = 0; i < elts; i++) {
90 target[3 * i + 0] = src[3 * i + 0];
91 target[3 * i + 1] = src[3 * i + 1];
92 target[3 * i + 2] = src[3 * i + 2];
94 } else if(c == 4) {
95 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
96 for(size_t i = 0; i < elts; i++) {
97 target[4 * i + 0] = _src[i] >> 24;
98 target[4 * i + 1] = _src[i] >> 16;
99 target[4 * i + 2] = _src[i] >> 8;
100 target[4 * i + 3] = _src[i];
102 } else if(c == 5) {
103 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
104 for(size_t i = 0; i < elts; i++) {
105 target[3 * i + 0] = _src[i] >> 16;
106 target[3 * i + 1] = _src[i] >> 8;
107 target[3 * i + 2] = _src[i];
112 struct color_modifier
114 const char* name;
115 std::function<void(int64_t& v)> fn;
116 bool modifier;
119 std::map<std::string, std::pair<std::function<void(int64_t& v)>, bool>>& colornames()
121 static std::map<std::string, std::pair<std::function<void(int64_t& v)>, bool>> c;
122 return c;
126 basecolor::basecolor(const std::string& name, int64_t value)
128 colornames()[name] = std::make_pair([value](int64_t& v) { v = value; }, false);
131 color_mod::color_mod(const std::string& name, std::function<void(int64_t&)> fn)
133 colornames()[name] = std::make_pair(fn, true);
136 pixfmt::pixfmt() throw(std::bad_alloc)
138 pixfmts().push_back(this);
141 pixfmt::~pixfmt() throw()
143 for(auto i = pixfmts().begin(); i != pixfmts().end(); i++)
144 if(*i == this) {
145 pixfmts().erase(i);
146 break;
150 raw::raw(const info& info) throw(std::bad_alloc)
152 size_t unit = info.type->get_bpp();
153 size_t pixel_offset = info.offset_y * info.physstride + unit * info.offset_x;
154 user_memory = false;
155 addr = info.mem + pixel_offset;
156 fmt = info.type;
157 width = info.width;
158 height = info.height;
159 stride = info.stride;
160 allocated = 0;
163 raw::raw() throw(std::bad_alloc)
165 user_memory = true;
166 fmt = NULL;
167 addr = NULL;
168 width = 0;
169 height = 0;
170 stride = 0;
171 allocated = 0;
174 raw::raw(const raw& f) throw(std::bad_alloc)
176 user_memory = true;
177 fmt = f.fmt;
178 width = f.width;
179 height = f.height;
180 size_t unit = f.fmt->get_bpp();
181 stride = f.width * unit;
182 allocated = unit * width * height;
183 addr = new char[allocated];
184 for(size_t i = 0; i < height; i++)
185 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
188 raw& raw::operator=(const raw& f) throw(std::bad_alloc, std::runtime_error)
190 if(!user_memory)
191 throw std::runtime_error("Target framebuffer is not writable");
192 if(this == &f)
193 return *this;
194 size_t unit = f.fmt->get_bpp();
195 size_t newallocated = unit * f.width * f.height;
196 if(newallocated > allocated) {
197 char* newaddr = new char[newallocated];
198 delete[] addr;
199 addr = newaddr;
200 allocated = newallocated;
202 fmt = f.fmt;
203 width = f.width;
204 height = f.height;
205 stride = f.width * unit;
206 for(size_t i = 0; i < height; i++)
207 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
208 return *this;
211 raw::~raw()
213 if(user_memory)
214 delete[] addr;
217 void raw::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
219 if(data.size() < 2)
220 throw std::runtime_error("Bad screenshot data");
221 if(!user_memory)
222 throw std::runtime_error("Target framebuffer is not writable");
223 pixfmt* nfmt = NULL;
224 const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
225 size_t legacy_width = serialization::u16b(data2);
226 size_t dataoffset;
227 size_t _width;
228 size_t _height;
230 if(legacy_width > 0 && data.size() % (3 * legacy_width) == 2) {
231 //Legacy screenshot.
232 for(pixfmt* f : pixfmts())
233 if(f->get_magic() == 0)
234 nfmt = f;
235 if(!nfmt)
236 throw std::runtime_error("Unknown screenshot format");
237 _width = legacy_width;
238 _height = (data.size() - 2) / (3 * legacy_width);
239 dataoffset = 2;
240 } else {
241 //New format.
242 if(data.size() < 8)
243 throw std::runtime_error("Bad screenshot data");
244 dataoffset = 8;
245 uint32_t magic = serialization::u32b(data2 + 2);
246 for(pixfmt* f : pixfmts())
247 if(f->get_magic() == magic)
248 nfmt = f;
249 if(!nfmt)
250 throw std::runtime_error("Unknown screenshot format");
251 _width = serialization::u16b(data2 + 6);
252 _height = (data.size() - 8) / (nfmt->get_ss_bpp() * _width);
254 if(data.size() < dataoffset + nfmt->get_ss_bpp() * _width * _height)
255 throw std::runtime_error("Bad screenshot data");
257 size_t bpp = nfmt->get_bpp();
258 size_t sbpp = nfmt->get_ss_bpp();
259 if(allocated < bpp * _width * _height) {
260 //Allocate more memory.
261 size_t newalloc = bpp * _width * _height;
262 char* addr2 = new char[newalloc];
263 delete[] addr;
264 addr = addr2;
265 allocated = newalloc;
267 fmt = nfmt;
268 width = _width;
269 height = _height;
270 stride = _width * bpp;
271 if(bpp == 1)
272 decode_words<1>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
273 else if(bpp == 2)
274 decode_words<2>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
275 else if(bpp == 3)
276 decode_words<3>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
277 else if(bpp == 4 && sbpp == 3)
278 decode_words<5>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
279 else if(bpp == 4 && sbpp == 4)
280 decode_words<4>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
283 void raw::save(std::vector<char>& data) throw(std::bad_alloc)
285 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
286 unsigned m;
287 size_t bpp = fmt->get_bpp();
288 size_t sbpp = fmt->get_ss_bpp();
289 size_t offset;
290 uint8_t* data2;
291 uint32_t magic = fmt->get_magic();
292 switch(magic) {
293 case 0:
294 //Save in legacy format.
295 offset = 2;
296 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
297 data2 = reinterpret_cast<uint8_t*>(&data[0]);
298 serialization::u16b(&data[0], width);
299 break;
300 default:
301 //Choose the first two bytes so that screenshot is bad in legacy format.
302 m = 2;
303 while((sbpp * width * height + 8) % (3 * m) == 2)
304 m++;
305 offset = 8;
306 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
307 serialization::u16b(&data[0], m);
308 serialization::u32b(&data[2], magic);
309 serialization::u16b(&data[6], width);
310 break;
312 data2 = reinterpret_cast<uint8_t*>(&data[0]);
313 for(size_t i = 0; i < height; i++) {
314 if(bpp == 1)
315 encode_words<1>(data2 + offset + sbpp * width * i, memory + stride * i, width);
316 else if(bpp == 2)
317 encode_words<2>(data2 + offset + sbpp * width * i, memory + stride * i, width);
318 else if(bpp == 3)
319 encode_words<3>(data2 + offset + sbpp * width * i, memory + stride * i, width);
320 else if(bpp == 4 && sbpp == 3)
321 encode_words<5>(data2 + offset + sbpp * width * i, memory + stride * i, width);
322 else if(bpp == 4 && sbpp == 4)
323 encode_words<4>(data2 + offset + sbpp * width * i, memory + stride * i, width);
327 void raw::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
329 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
330 png::encoder img;
331 img.width = width;
332 img.height = height;
333 img.has_palette = false;
334 img.has_alpha = false;
335 img.data.resize(static_cast<size_t>(width) * height);
336 for(size_t i = 0; i < height; i++)
337 fmt->decode(&img.data[width * i], memory + stride * i, width);
338 try {
339 img.encode(file);
340 } catch(...) {
341 throw;
345 size_t raw::get_stride() const throw() { return stride; }
346 unsigned char* raw::get_start() const throw() { return reinterpret_cast<uint8_t*>(addr); }
347 pixfmt* raw::get_format() const throw() { return fmt; }
350 template<bool X>
351 fb<X>::fb() throw()
353 width = 0;
354 height = 0;
355 last_blit_w = 0;
356 last_blit_h = 0;
357 stride = 0;
358 offset_x = 0;
359 offset_y = 0;
360 mem = NULL;
361 user_mem = false;
362 upside_down = false;
363 current_fmt = NULL;
364 recalculate_default_shifts();
365 active_rshift = default_shift_r << (X ? 1 : 0);
366 active_gshift = default_shift_g << (X ? 1 : 0);
367 active_bshift = default_shift_b << (X ? 1 : 0);
370 template<bool X>
371 fb<X>::~fb() throw()
373 if(user_mem)
374 delete[] mem;
377 #define DECBUF_SIZE 4096
379 template<bool X>
380 void fb<X>::copy_from(raw& scr, size_t hscale, size_t vscale) throw()
382 typename fb<X>::element_t decbuf[DECBUF_SIZE];
383 last_blit_w = scr.width * hscale;
384 last_blit_h = scr.height * vscale;
386 if(!scr.fmt) {
387 for(size_t y = 0; y < height; y++)
388 memset(rowptr(y), 0, sizeof(typename fb<X>::element_t) * width);
389 return;
391 if(scr.fmt != current_fmt || active_rshift != auxpal.rshift || active_gshift != auxpal.gshift ||
392 active_bshift != auxpal.bshift) {
393 scr.fmt->set_palette(auxpal, active_rshift, active_gshift, active_bshift);
394 current_fmt = scr.fmt;
397 for(size_t y = 0; y < height; y++)
398 memset(rowptr(y), 0, sizeof(typename fb<X>::element_t) * width);
399 if(width < offset_x || height < offset_y) {
400 //Just clear the screen.
401 return;
403 size_t copyable_width = 0, copyable_height = 0;
404 if(hscale)
405 copyable_width = (width - offset_x) / hscale;
406 if(vscale)
407 copyable_height = (height - offset_y) / vscale;
408 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
409 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
411 for(size_t y = 0; y < copyable_height; y++) {
412 size_t line = y * vscale + offset_y;
413 const uint8_t* sbase = reinterpret_cast<uint8_t*>(scr.addr) + y * scr.stride;
414 typename fb<X>::element_t* ptr = rowptr(line) + offset_x;
415 size_t bpp = scr.fmt->get_bpp();
416 size_t xptr = 0;
417 size_t old_copyable_width = copyable_width;
418 while(copyable_width > DECBUF_SIZE) {
419 scr.fmt->decode(decbuf, sbase + xptr * bpp, DECBUF_SIZE, auxpal);
420 for(size_t k = 0; k < DECBUF_SIZE; k++)
421 for(size_t i = 0; i < hscale; i++)
422 *(ptr++) = decbuf[k];
423 xptr += DECBUF_SIZE;
424 copyable_width -= DECBUF_SIZE;
426 scr.fmt->decode(decbuf, sbase + xptr * bpp, copyable_width, auxpal);
427 for(size_t k = 0; k < copyable_width; k++)
428 for(size_t i = 0; i < hscale; i++)
429 *(ptr++) = decbuf[k];
430 copyable_width = old_copyable_width;
431 for(size_t j = 1; j < vscale; j++)
432 memcpy(rowptr(line + j) + offset_x, rowptr(line) + offset_x,
433 sizeof(typename fb<X>::element_t) * hscale * copyable_width);
437 template<bool X>
438 void fb<X>::set_palette(uint32_t r, uint32_t g, uint32_t b) throw(std::bad_alloc)
440 typename fb<X>::element_t R, G, B;
441 if(r == active_rshift && g == active_gshift && b == active_bshift)
442 return;
443 for(size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
444 typename fb<X>::element_t word = mem[i];
445 R = (word >> active_rshift) & (X ? 0xFFFF : 0xFF);
446 G = (word >> active_gshift) & (X ? 0xFFFF : 0xFF);
447 B = (word >> active_bshift) & (X ? 0xFFFF : 0xFF);
448 mem[i] = (R << r) | (G << g) | (B << b);
450 active_rshift = r;
451 active_gshift = g;
452 active_bshift = b;
455 template<bool X>
456 void fb<X>::set(element_t* _memory, size_t _width, size_t _height, size_t _pitch) throw()
458 if(user_mem && mem)
459 delete[] mem;
460 mem = _memory;
461 width = _width;
462 height = _height;
463 stride = _pitch;
464 user_mem = false;
465 upside_down = false;
468 template<bool X>
469 void fb<X>::reallocate(size_t _width, size_t _height, bool _upside_down) throw(std::bad_alloc)
471 size_t ustride = (_width + 11) / 12 * 12;
472 if(width != _width || height != _height) {
473 if(user_mem) {
474 element_t* newmem = new element_t[ustride * _height + 4];
475 delete[] mem;
476 mem = newmem;
477 } else
478 mem = new element_t[ustride * _height + 4];
480 memset(mem, 0, sizeof(element_t) * ustride * _height);
481 width = _width;
482 height = _height;
483 stride = ustride;
484 upside_down = _upside_down;
485 user_mem = true;
488 template<bool X>
489 void fb<X>::set_origin(size_t _offset_x, size_t _offset_y) throw()
491 offset_x = _offset_x;
492 offset_y = _offset_y;
495 template<bool X>
496 typename fb<X>::element_t* fb<X>::rowptr(size_t row) throw()
498 if(upside_down)
499 row = height - row - 1;
500 uint32_t align = (16 - reinterpret_cast<size_t>(mem)) % 16 / 4;
501 return mem + stride * row + align;
504 template<bool X>
505 const typename fb<X>::element_t* fb<X>::rowptr(size_t row) const throw()
507 if(upside_down)
508 row = height - row - 1;
509 return mem + stride * row;
512 template<bool X> uint8_t fb<X>::get_palette_r() const throw() { return auxpal.rshift; }
513 template<bool X> uint8_t fb<X>::get_palette_g() const throw() { return auxpal.gshift; }
514 template<bool X> uint8_t fb<X>::get_palette_b() const throw() { return auxpal.bshift; }
516 size_t raw::get_width() const throw() { return width; }
517 size_t raw::get_height() const throw() { return height; }
518 template<bool X> size_t fb<X>::get_origin_x() const throw() { return offset_x; }
519 template<bool X> size_t fb<X>::get_origin_y() const throw() { return offset_y; }
521 void queue::add(struct object& obj) throw(std::bad_alloc)
523 struct node* n = reinterpret_cast<struct node*>(alloc(sizeof(node)));
524 n->obj = &obj;
525 n->next = NULL;
526 n->killed = false;
527 if(queue_tail)
528 queue_tail = queue_tail->next = n;
529 else
530 queue_head = queue_tail = n;
533 void queue::copy_from(queue& q) throw(std::bad_alloc)
535 struct node* tmp = q.queue_head;
536 while(tmp) {
537 try {
538 tmp->obj->clone(*this);
539 tmp = tmp->next;
540 } catch(...) {
545 template<bool X> void queue::run(struct fb<X>& scr) throw()
547 //Take queue lock in order to syncronize this with killing the queue.
548 threads::alock h(display_mutex);
549 struct node* tmp = queue_head;
550 while(tmp) {
551 try {
552 if(!tmp->killed)
553 (*(tmp->obj))(scr);
554 tmp = tmp->next;
555 } catch(...) {
560 void queue::clear() throw()
562 while(queue_head) {
563 if(!queue_head->killed)
564 queue_head->obj->~object();
565 queue_head = queue_head->next;
567 //Release all memory for reuse.
568 memory_allocated = 0;
569 pages = 0;
570 queue_tail = NULL;
573 void* queue::alloc(size_t block) throw(std::bad_alloc)
575 block = (block + 15) / 16 * 16;
576 if(block > RENDER_PAGE_SIZE)
577 throw std::bad_alloc();
578 if(pages == 0 || memory_allocated + block > pages * RENDER_PAGE_SIZE) {
579 memory_allocated = pages * RENDER_PAGE_SIZE;
580 memory[pages++];
582 void* mem = memory[memory_allocated / RENDER_PAGE_SIZE].content + (memory_allocated % RENDER_PAGE_SIZE);
583 memory_allocated += block;
584 return mem;
587 void queue::kill_request(void* obj) throw()
589 //Take queue lock in order to syncronize this with drawing.
590 threads::alock h(display_mutex);
591 struct node* tmp = queue_head;
592 while(tmp) {
593 try {
594 if(!tmp->killed && tmp->obj->kill_request(obj)) {
595 //Kill this request.
596 tmp->killed = true;
597 tmp->obj->~object();
599 tmp = tmp->next;
600 } catch(...) {
605 queue::queue() throw()
606 : tracker(memtracker::singleton(), render_page_id, sizeof(*this))
608 queue_head = NULL;
609 queue_tail = NULL;
610 memory_allocated = 0;
611 pages = 0;
614 queue::~queue() throw()
616 clear();
619 object::object() throw()
623 object::~object() throw()
627 bool object::kill_request_ifeq(void* myobj, void* killobj)
629 if(!killobj)
630 return false;
631 if(myobj == killobj)
632 return true;
633 return false;
636 bool object::kill_request(void* obj) throw()
638 return false;
641 font::font() throw(std::bad_alloc)
643 bad_glyph_data[0] = 0x018001AAU;
644 bad_glyph_data[1] = 0x01800180U;
645 bad_glyph_data[2] = 0x01800180U;
646 bad_glyph_data[3] = 0x55800180U;
647 bad_glyph.wide = false;
648 bad_glyph.data = bad_glyph_data;
651 void font::load_hex_glyph(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
653 char buf2[8];
654 std::string line(data, data + size);
655 size_t linelen = line.length();
657 //Check if the line is valid.
658 //Line format is <hex digits>:<32 or 64 hex digits>.
659 size_t splitter = line.find(':');
660 if(splitter >= linelen || !(splitter + 33 == linelen || splitter + 65 == linelen))
661 (stringfmt() << "Invalid line '" << line << "'").throwex(); //No :, or not 32/64 hexes after.
662 if(line.find_first_not_of("0123456789ABCDEFabcdef:") < line.length())
663 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Invalid character.
664 if(line.find(':', splitter + 1) < line.length())
665 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Second :.
666 if(splitter > 7)
667 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Too long codepoint.
669 std::string codepoint = line.substr(0, splitter);
670 std::string cdata = line.substr(splitter + 1);
671 strcpy(buf2, codepoint.c_str());
672 char* end2;
673 unsigned long cp = strtoul(buf2, &end2, 16);
674 if(*end2 || cp > 0x10FFFF)
675 (stringfmt() << "Invalid line '" << line << "'").throwex(); //Codepoint out of range.
676 glyphs[cp].wide = (cdata.length() == 64);
677 size_t p = memory.size();
678 for(size_t i = 0; i < cdata.length(); i += 8) {
679 char buf[9] = {0};
680 char* end;
681 for(size_t j = 0; j < 8; j++)
682 buf[j] = cdata[i + j];
683 memory.push_back(strtoul(buf, &end, 16));
685 glyphs[cp].offset = p;
688 void font::load_hex(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
690 const char* enddata = data + size;
691 while(data != enddata) {
692 size_t linesize = 0;
693 while(data + linesize != enddata && data[linesize] != '\n' && data[linesize] != '\r')
694 linesize++;
695 if(linesize && data[0] != '#')
696 load_hex_glyph(data, linesize);
697 data += linesize;
698 if(data != enddata)
699 data++;
701 memory.push_back(0);
702 memory.push_back(0);
703 memory.push_back(0);
704 memory.push_back(0);
705 glyphs[32].wide = false;
706 glyphs[32].offset = memory.size() - 4;
707 for(auto& i : glyphs)
708 i.second.data = &memory[i.second.offset];
711 const font::glyph& font::get_glyph(uint32_t glyph) throw()
713 if(glyphs.count(glyph))
714 return glyphs[glyph];
715 else
716 return bad_glyph;
719 std::set<uint32_t> font::get_glyphs_set()
721 std::set<uint32_t> out;
722 for(auto& i : glyphs)
723 out.insert(i.first);
724 return out;
727 const font::glyph& font::get_bad_glyph() throw()
729 return bad_glyph;
732 std::pair<size_t, size_t> font::get_metrics(const std::string& string, uint32_t xalign, bool xdbl, bool ydbl) throw()
734 size_t commit_width = 0;
735 size_t commit_height = 0;
736 for_each_glyph(string, xalign, xdbl, ydbl, [&commit_width, &commit_height](uint32_t x, uint32_t y,
737 const glyph& g, bool xdbl, bool ydbl) {
738 size_t w = 1 << ((g.wide ? 4 : 3) + (xdbl ? 1 : 0));
739 size_t h = 1 << (4 + (ydbl ? 1 : 0));
740 commit_width = std::max(commit_width, (size_t)(x + w));
741 commit_height = std::max(commit_height, (size_t)(y + h));
743 return std::make_pair(commit_width, commit_height);
746 std::vector<font::layout> font::dolayout(const std::string& string) throw(std::bad_alloc)
748 //First, calculate the number of glyphs to draw.
749 size_t chars = 0;
750 for_each_glyph(string, 0, false, false, [&chars](uint32_t x, uint32_t y, const glyph& g, bool xdbl,
751 bool ydbl) {
752 chars++;
754 //Allocate space.
755 std::vector<layout> l;
756 l.resize(chars);
757 size_t gtr = 0;
758 for_each_glyph(string, 0, false, false, [&l, &gtr](uint32_t x, uint32_t y, const glyph& g, bool xdbl,
759 bool ydbl) {
760 l[gtr].x = x;
761 l[gtr].y = y;
762 l[gtr++].dglyph = &g;
764 return l;
767 uint32_t font::get_width(const std::string& string)
769 uint32_t width = 0;
770 utf8::to32i(string.begin(), string.end(), lambda_output_iterator<int32_t>([this, &width](const int32_t cp) {
771 const glyph& g = get_glyph(cp);
772 const uint32_t tabs = TABSTOPS >> 3;
773 switch(cp) {
774 case 9:
775 width = (width + tabs) / tabs * tabs;
776 break;
777 case 10:
778 break;
779 default:
780 width += (g.wide ? 2 : 1);
782 }));
783 return width;
786 template<bool X> void font::render(struct fb<X>& scr, int32_t x, int32_t y, const std::string& text,
787 color fg, color bg, bool hdbl, bool vdbl) throw()
789 x += scr.get_origin_x();
790 y += scr.get_origin_y();
791 size_t swidth = scr.get_width();
792 size_t sheight = scr.get_height();
794 for_each_glyph(text, x, hdbl, vdbl, [this, x, y, &scr, swidth, sheight, &fg, &bg](uint32_t lx, uint32_t ly,
795 const glyph& g, bool hdbl, bool vdbl) {
796 //Render this glyph at x + lx, y + ly.
797 int32_t gx = x + lx;
798 int32_t gy = y + ly;
799 //Don't draw characters completely off-screen.
800 if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight)
801 return;
802 if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth)
803 return;
804 //Compute the bounding box.
805 uint32_t xstart = 0;
806 uint32_t ystart = 0;
807 uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
808 uint32_t ylength = (vdbl ? 32 : 16);
809 if(gx < 0) xstart = -gx;
810 if(gy < 0) ystart = -gy;
811 xlength -= xstart;
812 ylength -= ystart;
813 if(gx + xstart + xlength > swidth) xlength = swidth - (gx + xstart);
814 if(gy + ystart + ylength > sheight) ylength = sheight - (gy + ystart);
815 if(g.data)
816 for(size_t i = 0; i < ylength; i++) {
817 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) +
818 (gx + xstart);
819 uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0);
820 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
821 if(g.wide)
822 d >>= 16 - ((_y & 1) << 4);
823 else
824 d >>= 24 - ((_y & 3) << 3);
825 if(hdbl)
826 for(size_t j = 0; j < xlength; j++) {
827 uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1);
828 if(((d >> b) & 1) != 0)
829 fg.apply(r[j]);
830 else
831 bg.apply(r[j]);
833 else
834 for(size_t j = 0; j < xlength; j++) {
835 uint32_t b = (g.wide ? 15 : 7) - (j + xstart);
836 if(((d >> b) & 1) != 0)
837 fg.apply(r[j]);
838 else
839 bg.apply(r[j]);
842 else
843 for(size_t i = 0; i < ylength; i++) {
844 typename fb<X>::element_t* r = scr.rowptr(gy + ystart + i) + (gx + xstart);
845 for(size_t j = 0; j < xlength; j++)
846 bg.apply(r[j]);
851 void font::render(uint8_t* buf, size_t stride, const std::string& str, uint32_t alignx, bool hdbl, bool vdbl)
853 for_each_glyph(str, alignx, hdbl, vdbl, [this, buf, stride]
854 (uint32_t lx, uint32_t ly, const glyph& g, bool hdbl, bool vdbl) {
855 uint8_t* ptr = buf + (ly * stride + lx);
856 size_t xlength = (g.wide ? 16 : 8) << (hdbl ? 1 : 0);
857 size_t height = 16 << (vdbl ? 1 : 0);
859 if(g.data)
860 for(size_t i = 0; i < height; i++) {
861 ptr += stride;
862 uint32_t _y = i >> (vdbl ? 1 : 0);
863 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
864 if(g.wide)
865 d >>= 16 - ((_y & 1) << 4);
866 else
867 d >>= 24 - ((_y & 3) << 3);
868 if(hdbl)
869 for(size_t j = 0; j < xlength; j++) {
870 uint32_t b = (g.wide ? 15 : 7) - (j >> 1);
871 ptr[j] = ((d >> b) & 1);
873 else
874 for(size_t j = 0; j < xlength; j++) {
875 uint32_t b = (g.wide ? 15 : 7) - j;
876 ptr[j] = ((d >> b) & 1);
883 void font::for_each_glyph(const std::string& str, uint32_t alignx, bool xdbl, bool ydbl,
884 std::function<void(uint32_t x, uint32_t y, const glyph& g, bool xdbl, bool ydbl)> cb)
886 size_t layout_x = alignx;
887 size_t layout_y = 0;
888 size_t offset = alignx;
889 unsigned _xdbl = xdbl;
890 unsigned _ydbl = ydbl;
891 auto _cb = &cb;
892 utf8::to32i(str.begin(), str.end(), lambda_output_iterator<int32_t>([this, &layout_x, &layout_y, _xdbl,
893 _ydbl, offset, _cb](const int32_t cp) {
894 const glyph& g = get_glyph(cp);
895 switch(cp) {
896 case 9:
897 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
898 break;
899 case 10:
900 layout_x = 0;
901 layout_y = layout_y + 16;
902 break;
903 default:
904 (*_cb)((layout_x - offset) << _xdbl, layout_y << _ydbl, g, _xdbl, _ydbl);
905 layout_x = layout_x + (g.wide ? 16 : 8);
907 }));
911 color::color(const std::string& clr) throw(std::bad_alloc, std::runtime_error)
913 int64_t col = -1;
914 bool first = true;
915 auto& cspecs = colornames();
916 for(auto& t : token_iterator<char>::foreach(clr, {" ","\t"}, true)) {
917 if(t.length() > 0 && t[0] == '#') {
918 if(!first)
919 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
920 std::string _t = t;
921 col = hex::from<uint32_t>(_t.substr(1));
922 first = false;
923 continue;
925 if(!cspecs.count(t))
926 throw std::runtime_error("Invalid color (modifier) '" + t + "'");
927 if(!first && !cspecs[t].second)
928 throw std::runtime_error("Base color (" + t + ") can't be used as modifier");
929 if(first && cspecs[t].second)
930 throw std::runtime_error("Modifier (" + t + ") can't be used as base color");
931 (cspecs[t].first)(col);
932 first = false;
934 *this = color(col);
937 std::string color::stringify(int64_t number)
939 for(auto& i : colornames()) {
940 int64_t col = -1;
941 if(i.second.second)
942 continue;
943 (i.second.first)(col);
944 if(col == number)
945 return i.first;
947 if(number < 0)
948 return "transparent";
949 else if(number < 16777216)
950 return "#" + hex::to<uint32_t>(number).substr(2);
951 else
952 return "#" + hex::to<uint32_t>(number);
955 void color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift, bool X) throw()
957 if(X) {
958 uint64_t r = ((orig >> 16) & 0xFF) * 257;
959 uint64_t g = ((orig >> 8) & 0xFF) * 257;
960 uint64_t b = (orig & 0xFF) * 257;
961 uint64_t a = 65535;
962 uint64_t fullc = ~0ULL & ~((a << rshift) | (a << gshift) | (a << bshift));
963 uint64_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
964 hiHI = color & 0xFFFF0000FFFFULL;
965 loHI = (color & 0xFFFF0000FFFF0000ULL) >> 16;
966 hiHI *= (static_cast<uint32_t>(origa) * 256);
967 loHI *= (static_cast<uint32_t>(origa) * 256);
968 } else {
969 uint32_t r = (orig >> 16) & 0xFF;
970 uint32_t g = (orig >> 8) & 0xFF;
971 uint32_t b = orig & 0xFF;
972 uint32_t a = 255;
973 uint64_t fullc = ~0UL & ~((a << rshift) | (a << gshift) | (a << bshift));
974 uint32_t color = (r << rshift) | (g << gshift) | (b << bshift) | fullc;
975 hi = color & 0xFF00FF;
976 lo = (color & 0xFF00FF00) >> 8;
977 hi *= origa;
978 lo *= origa;
982 namespace
984 void adjust_hmM_hue(int16_t& hue, int16_t& m, int16_t& M, double adj)
986 if(m == M)
987 return;
988 int16_t S = M - m;
989 hue = (hue + static_cast<uint32_t>(adj * S)) % (6 * S);
991 void adjust_ls_saturation(double& s, double& l, double adj)
993 s = clip(s + adj, 0.0, 1.0);
995 void adjust_ls_lightness(double& s, double& l, double adj)
997 l = clip(l + adj, 0.0, 1.0);
999 template<void(*adjustfn)(double& s, double& l, double adj)>
1000 void adjust_hmM_sl(int16_t& hue, int16_t& m, int16_t& M, double adj)
1002 int16_t S1 = M - m;
1003 double _m = m / 255.0;
1004 double _M = M / 255.0;
1005 double l = (_m + _M) / 2;
1006 double s;
1007 if(l == 0 || l == 1) s = 0;
1008 else if(l <= 0.5) s = _M / l - 1;
1009 else s = (_M - l) / (1 - l);
1010 adjustfn(s, l, adj);
1011 if(l <= 0.5) _M = l * (s + 1);
1012 else _M = l + s - l * s;
1013 _m = 2 * l -_M;
1014 m = _m * 255;
1015 M = _M * 255;
1016 int32_t S2 = M - m;
1017 hue = S1 ? (S2 * hue / S1) : 0;
1019 //0: m
1020 //1: M
1021 //2: m + phue
1022 //3: M - phue
1023 const uint8_t hsl2rgb_flags[] = {24, 52, 6, 13, 33, 19};
1024 template<void(*adjustfn)(int16_t& hue, int16_t& m, int16_t& M, double adj)>
1025 uint32_t adjustcolor(uint32_t color, double shift)
1027 int16_t R = (color >> 16) & 0xFF;
1028 int16_t G = (color >> 8) & 0xFF;
1029 int16_t B = color & 0xFF;
1030 int16_t m = min(R, min(G, B));
1031 int16_t M = max(R, max(G, B));
1032 int16_t S1 = M - m;
1033 int16_t hue;
1034 if(R == M)
1035 hue = G - B + 6 * S1;
1036 else if(G == M)
1037 hue = B - R + 2 * S1;
1038 else
1039 hue = R - G + 4 * S1;
1040 adjustfn(hue, m, M, shift);
1041 if(m == M)
1042 return ((uint32_t)m << 16) | ((uint32_t)m << 8) | (uint32_t)m;
1043 int16_t S2 = M - m;
1044 hue %= (6 * S2);
1045 uint32_t V[4];
1046 V[0] = m;
1047 V[1] = M;
1048 V[2] = m + hue % S2;
1049 V[3] = M - hue % S2;
1050 uint8_t flag = hsl2rgb_flags[hue / S2];
1051 return (V[(flag >> 4) & 3] << 16) | (V[(flag >> 2) & 3] << 8) | (V[flag & 3]);
1055 int64_t color_rotate_hue(int64_t basecolor, int step, int steps)
1057 if(!steps)
1058 throw std::runtime_error("Expected nonzero steps for hue rotation");
1059 if(basecolor < 0) {
1060 //Special: Any rotation of transparent is transparent.
1061 return -1;
1063 uint32_t asteps = std::abs(steps);
1064 if(steps < 0)
1065 step = asteps - step % asteps; //Reverse order.
1066 double hueshift = 6.0 * (step % asteps) / asteps;
1067 basecolor = adjustcolor<adjust_hmM_hue>(basecolor & 0xFFFFFF, hueshift) | (basecolor & 0xFF000000);
1068 return basecolor;
1071 int64_t color_adjust_saturation(int64_t color, double adjust)
1073 if(color < 0) return color;
1074 return adjustcolor<adjust_hmM_sl<adjust_ls_saturation>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1077 int64_t color_adjust_lightness(int64_t color, double adjust)
1079 if(color < 0) return color;
1080 return adjustcolor<adjust_hmM_sl<adjust_ls_lightness>>(color & 0xFFFFFF, adjust) | (color & 0xFF000000);
1083 template class fb<false>;
1084 template class fb<true>;
1085 template void queue::run(struct fb<false>&);
1086 template void queue::run(struct fb<true>&);
1087 template void font::render(struct fb<false>& scr, int32_t x, int32_t y, const std::string& text,
1088 color fg, color bg, bool hdbl, bool vdbl) throw();
1089 template void font::render(struct fb<true>& scr, int32_t x, int32_t y, const std::string& text,
1090 color fg, color bg, bool hdbl, bool vdbl) throw();