Merge branch 'rr1-maint'
[lsnes.git] / src / library / framebuffer.cpp
blobb781e85f90419ec211efeedaa0c2053a313c6702
1 #include "framebuffer.hpp"
2 #include "png.hpp"
3 #include "serialization.hpp"
4 #include "string.hpp"
5 #include "utf8.hpp"
6 #include <cstring>
7 #include <iostream>
8 #include <list>
10 #define TABSTOPS 64
11 #define SCREENSHOT_RGB_MAGIC 0x74212536U
13 namespace
15 std::list<pixel_format*>& pixel_formats()
17 static std::list<pixel_format*> x;
18 return x;
21 template<size_t c> void decode_words(uint8_t* target, const uint8_t* src, size_t srcsize)
23 if(c == 1 || c == 2 || c == 3 || c == 4)
24 srcsize /= c;
25 else
26 srcsize /= 3;
27 if(c == 1) {
28 for(size_t i = 0; i < srcsize; i++)
29 target[i] = src[i];
30 } else if(c == 2) {
31 uint16_t* _target = reinterpret_cast<uint16_t*>(target);
32 for(size_t i = 0; i < srcsize; i++) {
33 _target[i] = static_cast<uint16_t>(src[2 * i + 0]) << 8;
34 _target[i] |= static_cast<uint16_t>(src[2 * i + 1]);
36 } else if(c == 3) {
37 for(size_t i = 0; i < srcsize; i++) {
38 target[3 * i + 0] = src[3 * i + 0];
39 target[3 * i + 1] = src[3 * i + 1];
40 target[3 * i + 2] = src[3 * i + 2];
42 } else if(c == 4) {
43 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
44 for(size_t i = 0; i < srcsize; i++) {
45 _target[i] = static_cast<uint32_t>(src[4 * i + 0]) << 24;
46 _target[i] |= static_cast<uint32_t>(src[4 * i + 1]) << 16;
47 _target[i] |= static_cast<uint32_t>(src[4 * i + 2]) << 8;
48 _target[i] |= static_cast<uint32_t>(src[4 * i + 3]);
50 } else if(c == 5) {
51 uint32_t* _target = reinterpret_cast<uint32_t*>(target);
52 for(size_t i = 0; i < srcsize; i++) {
53 _target[i] = (static_cast<uint32_t>(src[3 * i + 0]) << 16);
54 _target[i] |= (static_cast<uint32_t>(src[3 * i + 1]) << 8);
55 _target[i] |= (static_cast<uint32_t>(src[3 * i + 2]));
60 template<size_t c> void encode_words(uint8_t* target, const uint8_t* src, size_t elts)
62 if(c == 1)
63 for(size_t i = 0; i < elts; i++)
64 target[i] = src[i];
65 else if(c == 2) {
66 const uint16_t* _src = reinterpret_cast<const uint16_t*>(src);
67 for(size_t i = 0; i < elts; i++) {
68 target[2 * i + 0] = _src[i] >> 8;
69 target[2 * i + 1] = _src[i];
71 } else if(c == 3) {
72 for(size_t i = 0; i < elts; i++) {
73 target[3 * i + 0] = src[3 * i + 0];
74 target[3 * i + 1] = src[3 * i + 1];
75 target[3 * i + 2] = src[3 * i + 2];
77 } else if(c == 4) {
78 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
79 for(size_t i = 0; i < elts; i++) {
80 target[4 * i + 0] = _src[i] >> 24;
81 target[4 * i + 1] = _src[i] >> 16;
82 target[4 * i + 2] = _src[i] >> 8;
83 target[4 * i + 3] = _src[i];
85 } else if(c == 5) {
86 const uint32_t* _src = reinterpret_cast<const uint32_t*>(src);
87 for(size_t i = 0; i < elts; i++) {
88 target[3 * i + 0] = _src[i] >> 16;
89 target[3 * i + 1] = _src[i] >> 8;
90 target[3 * i + 2] = _src[i];
96 pixel_format::pixel_format() throw(std::bad_alloc)
98 pixel_formats().push_back(this);
101 pixel_format::~pixel_format() throw()
103 for(auto i = pixel_formats().begin(); i != pixel_formats().end(); i++)
104 if(*i == this) {
105 pixel_formats().erase(i);
106 break;
110 framebuffer_raw::framebuffer_raw(const framebuffer_info& info) throw(std::bad_alloc)
112 size_t unit = info.type->get_bpp();
113 size_t pixel_offset = info.offset_y * info.physstride + unit * info.offset_x;
114 user_memory = false;
115 addr = info.mem + pixel_offset;
116 fmt = info.type;
117 width = info.width;
118 height = info.height;
119 stride = info.stride;
120 allocated = 0;
123 framebuffer_raw::framebuffer_raw() throw(std::bad_alloc)
125 user_memory = true;
126 fmt = NULL;
127 addr = NULL;
128 width = 0;
129 height = 0;
130 stride = 0;
131 allocated = 0;
134 framebuffer_raw::framebuffer_raw(const framebuffer_raw& f) throw(std::bad_alloc)
136 user_memory = true;
137 fmt = f.fmt;
138 width = f.width;
139 height = f.height;
140 size_t unit = f.fmt->get_bpp();
141 stride = f.width * unit;
142 allocated = unit * width * height;
143 addr = new char[allocated];
144 for(size_t i = 0; i < height; i++)
145 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
148 framebuffer_raw& framebuffer_raw::operator=(const framebuffer_raw& f) throw(std::bad_alloc, std::runtime_error)
150 if(!user_memory)
151 throw std::runtime_error("Target framebuffer is not writable");
152 if(this == &f)
153 return *this;
154 size_t unit = f.fmt->get_bpp();
155 size_t newallocated = unit * f.width * f.height;
156 if(newallocated > allocated) {
157 char* newaddr = new char[newallocated];
158 delete[] addr;
159 addr = newaddr;
160 allocated = newallocated;
162 fmt = f.fmt;
163 width = f.width;
164 height = f.height;
165 stride = f.width * unit;
166 for(size_t i = 0; i < height; i++)
167 memcpy(addr + stride * i, f.addr + f.stride * i, unit * width);
168 return *this;
171 framebuffer_raw::~framebuffer_raw()
173 if(user_memory)
174 delete[] addr;
177 void framebuffer_raw::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
179 if(data.size() < 2)
180 throw std::runtime_error("Bad screenshot data");
181 if(!user_memory)
182 throw std::runtime_error("Target framebuffer is not writable");
183 pixel_format* nfmt = NULL;
184 const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
185 size_t legacy_width = read16ube(data2);
186 size_t dataoffset;
187 size_t _width;
188 size_t _height;
190 if(legacy_width > 0 && data.size() % (3 * legacy_width) == 2) {
191 //Legacy screenshot.
192 for(pixel_format* f : pixel_formats())
193 if(f->get_magic() == 0)
194 nfmt = f;
195 if(!nfmt)
196 throw std::runtime_error("Unknown screenshot format");
197 _width = legacy_width;
198 _height = (data.size() - 2) / (3 * legacy_width);
199 dataoffset = 2;
200 } else {
201 //New format.
202 if(data.size() < 8)
203 throw std::runtime_error("Bad screenshot data");
204 dataoffset = 8;
205 uint32_t magic = read32ube(data2 + 2);
206 for(pixel_format* f : pixel_formats())
207 if(f->get_magic() == magic)
208 nfmt = f;
209 if(!nfmt)
210 throw std::runtime_error("Unknown screenshot format");
211 _width = read16ube(data2 + 6);
212 _height = (data.size() - 8) / (nfmt->get_ss_bpp() * _width);
214 if(data.size() < dataoffset + nfmt->get_ss_bpp() * _width * _height)
215 throw std::runtime_error("Bad screenshot data");
217 size_t bpp = nfmt->get_bpp();
218 size_t sbpp = nfmt->get_ss_bpp();
219 if(allocated < bpp * _width * _height) {
220 //Allocate more memory.
221 size_t newalloc = bpp * _width * _height;
222 char* addr2 = new char[newalloc];
223 delete[] addr;
224 addr = addr2;
225 allocated = newalloc;
227 fmt = nfmt;
228 width = _width;
229 height = _height;
230 stride = _width * bpp;
231 if(bpp == 1)
232 decode_words<1>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
233 else if(bpp == 2)
234 decode_words<2>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
235 else if(bpp == 3)
236 decode_words<3>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
237 else if(bpp == 4 && sbpp == 3)
238 decode_words<5>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
239 else if(bpp == 4 && sbpp == 4)
240 decode_words<4>(reinterpret_cast<uint8_t*>(addr), data2 + dataoffset, data.size() - dataoffset);
243 void framebuffer_raw::save(std::vector<char>& data) throw(std::bad_alloc)
245 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
246 unsigned m;
247 size_t bpp = fmt->get_bpp();
248 size_t sbpp = fmt->get_ss_bpp();
249 size_t offset;
250 uint8_t* data2;
251 uint32_t magic = fmt->get_magic();
252 switch(magic) {
253 case 0:
254 //Save in legacy format.
255 offset = 2;
256 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
257 data2 = reinterpret_cast<uint8_t*>(&data[0]);
258 write16ube(&data[0], width);
259 break;
260 default:
261 //Choose the first two bytes so that screenshot is bad in legacy format.
262 m = 2;
263 while(width * height % m == 0)
264 m++;
265 offset = 8;
266 data.resize(offset + sbpp * static_cast<size_t>(width) * height);
267 write16ube(&data[0], m);
268 write32ube(&data[2], magic);
269 write16ube(&data[6], width);
270 break;
272 data2 = reinterpret_cast<uint8_t*>(&data[0]);
273 for(size_t i = 0; i < height; i++) {
274 if(bpp == 1)
275 encode_words<1>(data2 + offset + sbpp * width * i, memory + stride * i, width);
276 else if(bpp == 2)
277 encode_words<2>(data2 + offset + sbpp * width * i, memory + stride * i, width);
278 else if(bpp == 3)
279 encode_words<3>(data2 + offset + sbpp * width * i, memory + stride * i, width);
280 else if(bpp == 4 && sbpp == 3)
281 encode_words<5>(data2 + offset + sbpp * width * i, memory + stride * i, width);
282 else if(bpp == 4 && sbpp == 4)
283 encode_words<4>(data2 + offset + sbpp * width * i, memory + stride * i, width);
287 void framebuffer_raw::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
289 uint8_t* memory = reinterpret_cast<uint8_t*>(addr);
290 uint8_t* buffer = new uint8_t[3 * static_cast<size_t>(width) * height];
291 for(size_t i = 0; i < height; i++)
292 fmt->decode(buffer + 3 * width * i, memory + stride * i, width);
293 try {
294 save_png_data(file, buffer, width, height);
295 delete[] buffer;
296 } catch(...) {
297 delete[] buffer;
298 throw;
302 template<bool X>
303 framebuffer<X>::framebuffer() throw()
305 width = 0;
306 height = 0;
307 stride = 0;
308 offset_x = 0;
309 offset_y = 0;
310 mem = NULL;
311 user_mem = false;
312 upside_down = false;
313 current_fmt = NULL;
314 active_rshift = (X ? 32 : 16);
315 active_gshift = (X ? 16 : 8);
316 active_bshift = 0;
320 template<bool X>
321 framebuffer<X>::~framebuffer() throw()
323 if(user_mem)
324 delete[] mem;
327 #define DECBUF_SIZE 1024
329 template<bool X>
330 void framebuffer<X>::copy_from(framebuffer_raw& scr, size_t hscale, size_t vscale) throw()
332 typename framebuffer<X>::element_t decbuf[DECBUF_SIZE];
334 if(!scr.fmt) {
335 for(size_t y = 0; y < height; y++)
336 memset(rowptr(y), 0, sizeof(typename framebuffer<X>::element_t) * width);
337 return;
339 if(scr.fmt != current_fmt || active_rshift != auxpal.rshift || active_gshift != auxpal.gshift ||
340 active_bshift != auxpal.bshift) {
341 scr.fmt->set_palette(auxpal, active_rshift, active_gshift, active_bshift);
342 current_fmt = scr.fmt;
345 for(size_t y = 0; y < height; y++)
346 memset(rowptr(y), 0, sizeof(typename framebuffer<X>::element_t) * width);
347 if(width < offset_x || height < offset_y) {
348 //Just clear the screen.
349 return;
351 size_t copyable_width = 0, copyable_height = 0;
352 if(hscale)
353 copyable_width = (width - offset_x) / hscale;
354 if(vscale)
355 copyable_height = (height - offset_y) / vscale;
356 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
357 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
359 for(size_t y = 0; y < copyable_height; y++) {
360 size_t line = y * vscale + offset_y;
361 const uint8_t* sbase = reinterpret_cast<uint8_t*>(scr.addr) + y * scr.stride;
362 typename framebuffer<X>::element_t* ptr = rowptr(line) + offset_x;
363 size_t bpp = scr.fmt->get_bpp();
364 size_t xptr = 0;
365 while(copyable_width > DECBUF_SIZE) {
366 scr.fmt->decode(decbuf, sbase + xptr * bpp, DECBUF_SIZE, auxpal);
367 for(size_t k = 0; k < DECBUF_SIZE; k++)
368 for(size_t i = 0; i < hscale; i++)
369 *(ptr++) = decbuf[k];
370 xptr += DECBUF_SIZE;
371 copyable_width -= DECBUF_SIZE;
373 scr.fmt->decode(decbuf, sbase + xptr * bpp, copyable_width, auxpal);
374 for(size_t k = 0; k < copyable_width; k++)
375 for(size_t i = 0; i < hscale; i++)
376 *(ptr++) = decbuf[k];
377 for(size_t j = 1; j < vscale; j++)
378 memcpy(rowptr(line + j) + offset_x, rowptr(line) + offset_x,
379 sizeof(typename framebuffer<X>::element_t) * hscale * copyable_width);
383 template<bool X>
384 void framebuffer<X>::set_palette(uint32_t r, uint32_t g, uint32_t b) throw(std::bad_alloc)
386 typename framebuffer<X>::element_t R, G, B;
387 if(r == active_rshift && g == active_gshift && b == active_bshift)
388 return;
389 for(size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
390 typename framebuffer<X>::element_t word = mem[i];
391 R = (word >> active_rshift) & (X ? 0xFFFF : 0xFF);
392 G = (word >> active_gshift) & (X ? 0xFFFF : 0xFF);
393 B = (word >> active_bshift) & (X ? 0xFFFF : 0xFF);
394 mem[i] = (R << r) | (G << g) | (B << b);
396 active_rshift = r;
397 active_gshift = g;
398 active_bshift = b;
401 template<bool X>
402 void framebuffer<X>::set(element_t* _memory, size_t _width, size_t _height, size_t _pitch) throw()
404 if(user_mem && mem)
405 delete[] mem;
406 mem = _memory;
407 width = _width;
408 height = _height;
409 stride = _pitch;
410 user_mem = false;
411 upside_down = false;
414 template<bool X>
415 void framebuffer<X>::reallocate(size_t _width, size_t _height, bool _upside_down) throw(std::bad_alloc)
417 if(width != _width || height != _height) {
418 if(user_mem) {
419 element_t* newmem = new element_t[_width * _height];
420 delete[] mem;
421 mem = newmem;
422 } else
423 mem = new element_t[_width * _height];
425 memset(mem, 0, sizeof(element_t) * _width * _height);
426 width = _width;
427 height = _height;
428 stride = _width;
429 upside_down = _upside_down;
430 user_mem = true;
433 template<bool X>
434 void framebuffer<X>::set_origin(size_t _offset_x, size_t _offset_y) throw()
436 offset_x = _offset_x;
437 offset_y = _offset_y;
440 template<bool X>
441 size_t framebuffer<X>::get_width() const throw()
443 return width;
446 template<bool X>
447 size_t framebuffer<X>::get_height() const throw()
449 return height;
452 template<bool X>
453 typename framebuffer<X>::element_t* framebuffer<X>::rowptr(size_t row) throw()
455 if(upside_down)
456 row = height - row - 1;
457 return mem + stride * row;
460 template<bool X>
461 const typename framebuffer<X>::element_t* framebuffer<X>::rowptr(size_t row) const throw()
463 if(upside_down)
464 row = height - row - 1;
465 return mem + stride * row;
468 template<bool X> uint8_t framebuffer<X>::get_palette_r() const throw() { return auxpal.rshift; }
469 template<bool X> uint8_t framebuffer<X>::get_palette_g() const throw() { return auxpal.gshift; }
470 template<bool X> uint8_t framebuffer<X>::get_palette_b() const throw() { return auxpal.bshift; }
472 size_t framebuffer_raw::get_width() const throw() { return width; }
473 size_t framebuffer_raw::get_height() const throw() { return height; }
474 template<bool X> size_t framebuffer<X>::get_origin_x() const throw() { return offset_x; }
475 template<bool X> size_t framebuffer<X>::get_origin_y() const throw() { return offset_y; }
477 void clip_range(uint32_t origin, uint32_t size, int32_t base, int32_t& minc, int32_t& maxc) throw()
479 int64_t _origin = origin;
480 int64_t _size = size;
481 int64_t _base = base;
482 int64_t _minc = minc;
483 int64_t _maxc = maxc;
484 int64_t mincoordinate = _base + _origin + _minc;
485 int64_t maxcoordinate = _base + _origin + _maxc;
486 if(mincoordinate < 0)
487 _minc = _minc - mincoordinate;
488 if(maxcoordinate > _size)
489 _maxc = _maxc - (maxcoordinate - _size);
490 if(_minc >= maxc) {
491 minc = 0;
492 maxc = 0;
493 } else {
494 minc = _minc;
495 maxc = _maxc;
499 void render_queue::add(struct render_object& obj) throw(std::bad_alloc)
501 struct node* n = reinterpret_cast<struct node*>(alloc(sizeof(node)));
502 n->obj = &obj;
503 n->next = NULL;
504 n->killed = false;
505 if(queue_tail)
506 queue_tail = queue_tail->next = n;
507 else
508 queue_head = queue_tail = n;
511 void render_queue::copy_from(render_queue& q) throw(std::bad_alloc)
513 struct node* tmp = q.queue_head;
514 while(tmp) {
515 try {
516 tmp->obj->clone(*this);
517 tmp = tmp->next;
518 } catch(...) {
523 template<bool X> void render_queue::run(struct framebuffer<X>& scr) throw()
525 struct node* tmp = queue_head;
526 while(tmp) {
527 try {
528 if(!tmp->killed)
529 (*(tmp->obj))(scr);
530 tmp = tmp->next;
531 } catch(...) {
536 void render_queue::clear() throw()
538 while(queue_head) {
539 if(!queue_head->killed)
540 queue_head->obj->~render_object();
541 queue_head = queue_head->next;
543 //Release all memory for reuse.
544 memory_allocated = 0;
545 pages = 0;
546 queue_tail = NULL;
549 void* render_queue::alloc(size_t block) throw(std::bad_alloc)
551 block = (block + 15) / 16 * 16;
552 if(block > RENDER_PAGE_SIZE)
553 throw std::bad_alloc();
554 if(pages == 0 || memory_allocated + block > pages * RENDER_PAGE_SIZE) {
555 memory_allocated = pages * RENDER_PAGE_SIZE;
556 memory[pages++];
558 void* mem = memory[memory_allocated / RENDER_PAGE_SIZE].content + (memory_allocated % RENDER_PAGE_SIZE);
559 memory_allocated += block;
560 return mem;
563 void render_queue::kill_request(void* obj) throw()
565 struct node* tmp = queue_head;
566 while(tmp) {
567 try {
568 if(!tmp->killed && tmp->obj->kill_request(obj)) {
569 //Kill this request.
570 tmp->killed = true;
571 tmp->obj->~render_object();
573 tmp = tmp->next;
574 } catch(...) {
579 render_queue::render_queue() throw()
581 queue_head = NULL;
582 queue_tail = NULL;
583 memory_allocated = 0;
584 pages = 0;
587 render_queue::~render_queue() throw()
589 clear();
592 render_object::render_object() throw()
596 render_object::~render_object() throw()
600 bool render_object::kill_request_ifeq(void* myobj, void* killobj)
602 if(!killobj)
603 return false;
604 if(myobj == killobj)
605 return true;
606 return false;
609 bool render_object::kill_request(void* obj) throw()
611 return false;
614 bitmap_font::bitmap_font() throw(std::bad_alloc)
616 bad_glyph_data[0] = 0x018001AAU;
617 bad_glyph_data[1] = 0x01800180U;
618 bad_glyph_data[2] = 0x01800180U;
619 bad_glyph_data[3] = 0x55800180U;
620 bad_glyph.wide = false;
621 bad_glyph.data = bad_glyph_data;
624 void bitmap_font::load_hex_glyph(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
626 char buf2[8];
627 std::string line(data, data + size);
628 regex_results r;
629 if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{32})", line)) {
630 } else if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{64})", line)) {
631 } else
632 (stringfmt() << "Invalid line '" << line << "'").throwex();
633 std::string codepoint = r[1];
634 std::string cdata = r[2];
635 if(codepoint.length() > 7)
636 (stringfmt() << "Invalid line '" << line << "'").throwex();
637 strcpy(buf2, codepoint.c_str());
638 char* end2;
639 unsigned long cp = strtoul(buf2, &end2, 16);
640 if(*end2 || cp > 0x10FFFF)
641 (stringfmt() << "Invalid line '" << line << "'").throwex();
642 glyphs[cp].wide = (cdata.length() == 64);
643 size_t p = memory.size();
644 for(size_t i = 0; i < cdata.length(); i += 8) {
645 char buf[9] = {0};
646 char* end;
647 for(size_t j = 0; j < 8; j++)
648 buf[j] = cdata[i + j];
649 memory.push_back(strtoul(buf, &end, 16));
651 glyphs[cp].offset = p;
654 void bitmap_font::load_hex(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
656 const char* enddata = data + size;
657 while(data != enddata) {
658 size_t linesize = 0;
659 while(data + linesize != enddata && data[linesize] != '\n' && data[linesize] != '\r')
660 linesize++;
661 if(linesize && data[0] != '#')
662 load_hex_glyph(data, linesize);
663 data += linesize;
664 if(data != enddata)
665 data++;
667 memory.push_back(0);
668 memory.push_back(0);
669 memory.push_back(0);
670 memory.push_back(0);
671 glyphs[32].wide = false;
672 glyphs[32].offset = memory.size() - 4;
673 for(auto& i : glyphs)
674 i.second.data = &memory[i.second.offset];
677 const bitmap_font::glyph& bitmap_font::get_glyph(uint32_t glyph) throw()
679 if(glyphs.count(glyph))
680 return glyphs[glyph];
681 else
682 return bad_glyph;
685 std::pair<size_t, size_t> bitmap_font::get_metrics(const std::string& string) throw()
687 size_t commit_width = 0;
688 size_t commit_height = 0;
689 int32_t lineminy = 0;
690 int32_t linemaxy = 0;
691 size_t linelength = 0;
692 uint16_t utfstate = utf8_initial_state;
693 size_t itr = 0;
694 size_t maxitr = string.length();
695 while(true) {
696 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
697 int32_t cp = utf8_parse_byte(ch, utfstate);
698 if(cp < 0 && ch < 0) {
699 //The end.
700 commit_width = (commit_width < linelength) ? linelength : commit_width;
701 commit_height += (linemaxy - lineminy + 1);
702 break;
704 if(cp < 0)
705 continue;
706 const glyph& g = get_glyph(cp);
707 switch(cp) {
708 case 9:
709 linelength = (linelength + TABSTOPS) / TABSTOPS * TABSTOPS;
710 break;
711 case 10:
712 commit_width = (commit_width < linelength) ? linelength : commit_width;
713 commit_height += 16;
714 break;
715 default:
716 linelength = linelength + (g.wide ? 16 : 8);
717 break;
720 return std::make_pair(commit_width, commit_height);
723 std::vector<bitmap_font::layout> bitmap_font::dolayout(const std::string& string) throw(std::bad_alloc)
725 //First, calculate the number of glyphs to draw.
726 uint16_t utfstate = utf8_initial_state;
727 size_t itr = 0;
728 size_t maxitr = string.length();
729 size_t chars = 0;
730 while(true) {
731 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
732 int32_t cp = utf8_parse_byte(ch, utfstate);
733 if(cp < 0 && ch < 0)
734 break;
735 if(cp != 9 && cp != 10)
736 chars++;
738 //Allocate space.
739 std::vector<layout> l;
740 l.resize(chars);
741 itr = 0;
742 size_t gtr = 0;
743 size_t layout_x = 0;
744 size_t layout_y = 0;
745 utfstate = utf8_initial_state;
746 while(true) {
747 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
748 int32_t cp = utf8_parse_byte(ch, utfstate);
749 if(cp < 0 && ch < 0)
750 break;
751 const glyph& g = get_glyph(cp);
752 switch(cp) {
753 case 9:
754 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
755 break;
756 case 10:
757 layout_x = 0;
758 layout_y = layout_y + 16;
759 break;
760 default:
761 l[gtr].x = layout_x;
762 l[gtr].y = layout_y;
763 l[gtr++].dglyph = &g;
764 layout_x = layout_x + (g.wide ? 16 : 8);;
767 return l;
770 template<bool X> void bitmap_font::render(struct framebuffer<X>& scr, int32_t x, int32_t y, const std::string& text,
771 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw()
773 x += scr.get_origin_x();
774 y += scr.get_origin_y();
775 uint16_t utfstate = utf8_initial_state;
776 size_t itr = 0;
777 size_t maxitr = text.length();
778 size_t layout_x = 0;
779 size_t layout_y = 0;
780 size_t swidth = scr.get_width();
781 size_t sheight = scr.get_width();
782 while(true) {
783 int ch = (itr < maxitr) ? static_cast<unsigned char>(text[itr++]) : -1;
784 int32_t cp = utf8_parse_byte(ch, utfstate);
785 if(cp < 0 && ch < 0)
786 break;
787 const glyph& g = get_glyph(cp);
788 switch(cp) {
789 case 9:
790 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
791 break;
792 case 10:
793 layout_x = 0;
794 layout_y = layout_y + (vdbl ? 32 : 16);
795 break;
796 default:
797 //Render this glyph at x + layout_x, y + layout_y.
798 int32_t gx = x + layout_x;
799 int32_t gy = y + layout_y;
800 //Don't draw characters completely off-screen.
801 if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight)
802 break;
803 if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth)
804 break;
805 //Compute the bounding box.
806 uint32_t xstart = 0;
807 uint32_t ystart = 0;
808 uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
809 uint32_t ylength = (vdbl ? 32 : 16);
810 if(gx < 0) xstart = -gx;
811 if(gy < 0) ystart = -gy;
812 xlength -= xstart;
813 ylength -= ystart;
814 if(gx + xlength > swidth) xlength = swidth - gx;
815 if(gy + ylength > sheight) ylength = sheight - gy;
816 if(g.data)
817 for(size_t i = 0; i < ylength; i++) {
818 typename framebuffer<X>::element_t* r = scr.rowptr(gy + ystart + i) +
819 (gx + xstart);
820 uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0);
821 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
822 if(g.wide)
823 d >>= 16 - ((_y & 1) << 4);
824 else
825 d >>= 24 - ((_y & 3) << 3);
826 if(hdbl)
827 for(size_t j = 0; j < xlength; j++) {
828 uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1);
829 if(((d >> b) & 1) != 0)
830 fg.apply(r[j]);
831 else
832 bg.apply(r[j]);
834 else
835 for(size_t j = 0; j < xlength; j++) {
836 uint32_t b = (g.wide ? 15 : 7) - (j + xstart);
837 if(((d >> b) & 1) != 0)
838 fg.apply(r[j]);
839 else
840 bg.apply(r[j]);
843 else
844 for(size_t i = 0; i < ylength; i++) {
845 typename framebuffer<X>::element_t* r = scr.rowptr(gy + ystart + i) +
846 (gx + xstart);
847 for(size_t j = 0; j < xlength; j++)
848 bg.apply(r[j]);
850 layout_x += (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
855 void premultiplied_color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift, bool X) throw()
857 if(X) {
858 uint64_t r = ((orig >> 16) & 0xFF) * 257;
859 uint64_t g = ((orig >> 8) & 0xFF) * 257;
860 uint64_t b = (orig & 0xFF) * 257;
861 uint64_t color = (r << rshift) | (g << gshift) | (b << bshift);
862 hiHI = color & 0xFFFF0000FFFFULL;
863 loHI = (color & 0xFFFF0000FFFF0000ULL) >> 16;
864 hiHI *= (static_cast<uint32_t>(origa) * 256);
865 loHI *= (static_cast<uint32_t>(origa) * 256);
866 } else {
867 uint32_t r = (orig >> 16) & 0xFF;
868 uint32_t g = (orig >> 8) & 0xFF;
869 uint32_t b = orig & 0xFF;
870 uint32_t color = (r << rshift) | (g << gshift) | (b << bshift);
871 hi = color & 0xFF00FF;
872 lo = (color & 0xFF00FF00) >> 8;
873 hi *= origa;
874 lo *= origa;
878 template class framebuffer<false>;
879 template class framebuffer<true>;
880 template void render_queue::run(struct framebuffer<false>&);
881 template void render_queue::run(struct framebuffer<true>&);
882 template void bitmap_font::render(struct framebuffer<false>& scr, int32_t x, int32_t y, const std::string& text,
883 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw();
884 template void bitmap_font::render(struct framebuffer<true>& scr, int32_t x, int32_t y, const std::string& text,
885 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw();