Movie editor: Fix compilation on Win32
[lsnes.git] / src / library / framebuffer.cpp
blob7aa5c5093ea6ff5407a633457e2a573746977706
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 if(queue_tail)
505 queue_tail = queue_tail->next = n;
506 else
507 queue_head = queue_tail = n;
510 template<bool X> void render_queue::run(struct framebuffer<X>& scr) throw()
512 struct node* tmp = queue_head;
513 while(tmp) {
514 try {
515 (*(tmp->obj))(scr);
516 tmp = tmp->next;
517 } catch(...) {
522 void render_queue::clear() throw()
524 while(queue_head) {
525 queue_head->obj->~render_object();
526 queue_head = queue_head->next;
528 //Release all memory for reuse.
529 memory_allocated = 0;
530 pages = 0;
531 queue_tail = NULL;
534 void* render_queue::alloc(size_t block) throw(std::bad_alloc)
536 block = (block + 15) / 16 * 16;
537 if(block > RENDER_PAGE_SIZE)
538 throw std::bad_alloc();
539 if(pages == 0 || memory_allocated + block > pages * RENDER_PAGE_SIZE) {
540 memory_allocated = pages * RENDER_PAGE_SIZE;
541 memory[pages++];
543 void* mem = memory[memory_allocated / RENDER_PAGE_SIZE].content + (memory_allocated % RENDER_PAGE_SIZE);
544 memory_allocated += block;
545 return mem;
548 render_queue::render_queue() throw()
550 queue_head = NULL;
551 queue_tail = NULL;
552 memory_allocated = 0;
553 pages = 0;
556 render_queue::~render_queue() throw()
558 clear();
561 render_object::~render_object() throw()
565 bitmap_font::bitmap_font() throw(std::bad_alloc)
567 bad_glyph_data[0] = 0x018001AAU;
568 bad_glyph_data[1] = 0x01800180U;
569 bad_glyph_data[2] = 0x01800180U;
570 bad_glyph_data[3] = 0x55800180U;
571 bad_glyph.wide = false;
572 bad_glyph.data = bad_glyph_data;
575 void bitmap_font::load_hex_glyph(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
577 char buf2[8];
578 std::string line(data, data + size);
579 regex_results r;
580 if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{32})", line)) {
581 } else if(r = regex("([0-9A-Fa-f]+):([0-9A-Fa-f]{64})", line)) {
582 } else
583 (stringfmt() << "Invalid line '" << line << "'").throwex();
584 std::string codepoint = r[1];
585 std::string cdata = r[2];
586 if(codepoint.length() > 7)
587 (stringfmt() << "Invalid line '" << line << "'").throwex();
588 strcpy(buf2, codepoint.c_str());
589 char* end2;
590 unsigned long cp = strtoul(buf2, &end2, 16);
591 if(*end2 || cp > 0x10FFFF)
592 (stringfmt() << "Invalid line '" << line << "'").throwex();
593 glyphs[cp].wide = (cdata.length() == 64);
594 size_t p = memory.size();
595 for(size_t i = 0; i < cdata.length(); i += 8) {
596 char buf[9] = {0};
597 char* end;
598 for(size_t j = 0; j < 8; j++)
599 buf[j] = cdata[i + j];
600 memory.push_back(strtoul(buf, &end, 16));
602 glyphs[cp].offset = p;
605 void bitmap_font::load_hex(const char* data, size_t size) throw(std::bad_alloc, std::runtime_error)
607 const char* enddata = data + size;
608 uint32_t lineno = 0;
609 while(data != enddata) {
610 size_t linesize = 0;
611 while(data + linesize != enddata && data[linesize] != '\n' && data[linesize] != '\r')
612 linesize++;
613 if(linesize && data[0] != '#')
614 load_hex_glyph(data, linesize);
615 data += linesize;
616 if(data != enddata)
617 data++;
619 memory.push_back(0);
620 memory.push_back(0);
621 memory.push_back(0);
622 memory.push_back(0);
623 glyphs[32].wide = false;
624 glyphs[32].offset = memory.size() - 4;
625 for(auto& i : glyphs)
626 i.second.data = &memory[i.second.offset];
629 const bitmap_font::glyph& bitmap_font::get_glyph(uint32_t glyph) throw()
631 if(glyphs.count(glyph))
632 return glyphs[glyph];
633 else
634 return bad_glyph;
637 std::pair<size_t, size_t> bitmap_font::get_metrics(const std::string& string) throw()
639 size_t commit_width = 0;
640 size_t commit_height = 0;
641 int32_t lineminy = 0;
642 int32_t linemaxy = 0;
643 size_t linelength = 0;
644 uint16_t utfstate = utf8_initial_state;
645 size_t itr = 0;
646 size_t maxitr = string.length();
647 while(true) {
648 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
649 int32_t cp = utf8_parse_byte(ch, utfstate);
650 if(cp < 0 && ch < 0) {
651 //The end.
652 commit_width = (commit_width < linelength) ? linelength : commit_width;
653 commit_height += (linemaxy - lineminy + 1);
654 break;
656 if(cp < 0)
657 continue;
658 const glyph& g = get_glyph(cp);
659 switch(cp) {
660 case 9:
661 linelength = (linelength + TABSTOPS) / TABSTOPS * TABSTOPS;
662 break;
663 case 10:
664 commit_width = (commit_width < linelength) ? linelength : commit_width;
665 commit_height += 16;
666 break;
667 default:
668 linelength = linelength + (g.wide ? 16 : 8);
669 break;
672 return std::make_pair(commit_width, commit_height);
675 std::vector<bitmap_font::layout> bitmap_font::dolayout(const std::string& string) throw(std::bad_alloc)
677 //First, calculate the number of glyphs to draw.
678 uint16_t utfstate = utf8_initial_state;
679 size_t itr = 0;
680 size_t maxitr = string.length();
681 size_t chars = 0;
682 while(true) {
683 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
684 int32_t cp = utf8_parse_byte(ch, utfstate);
685 if(cp < 0 && ch < 0)
686 break;
687 if(cp != 9 && cp != 10)
688 chars++;
690 //Allocate space.
691 std::vector<layout> l;
692 l.resize(chars);
693 itr = 0;
694 size_t gtr = 0;
695 size_t layout_x = 0;
696 size_t layout_y = 0;
697 utfstate = utf8_initial_state;
698 while(true) {
699 int ch = (itr < maxitr) ? static_cast<unsigned char>(string[itr++]) : -1;
700 int32_t cp = utf8_parse_byte(ch, utfstate);
701 if(cp < 0 && ch < 0)
702 break;
703 const glyph& g = get_glyph(cp);
704 switch(cp) {
705 case 9:
706 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
707 break;
708 case 10:
709 layout_x = 0;
710 layout_y = layout_y + 16;
711 break;
712 default:
713 l[gtr].x = layout_x;
714 l[gtr].y = layout_y;
715 l[gtr++].dglyph = &g;
716 layout_x = layout_x + (g.wide ? 16 : 8);;
719 return l;
722 template<bool X> void bitmap_font::render(struct framebuffer<X>& scr, int32_t x, int32_t y, const std::string& text,
723 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw()
725 x += scr.get_origin_x();
726 y += scr.get_origin_y();
727 uint16_t utfstate = utf8_initial_state;
728 size_t itr = 0;
729 size_t maxitr = text.length();
730 size_t layout_x = 0;
731 size_t layout_y = 0;
732 size_t swidth = scr.get_width();
733 size_t sheight = scr.get_width();
734 while(true) {
735 int ch = (itr < maxitr) ? static_cast<unsigned char>(text[itr++]) : -1;
736 int32_t cp = utf8_parse_byte(ch, utfstate);
737 if(cp < 0 && ch < 0)
738 break;
739 const glyph& g = get_glyph(cp);
740 switch(cp) {
741 case 9:
742 layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS;
743 break;
744 case 10:
745 layout_x = 0;
746 layout_y = layout_y + (vdbl ? 32 : 16);
747 break;
748 default:
749 //Render this glyph at x + layout_x, y + layout_y.
750 int32_t gx = x + layout_x;
751 int32_t gy = y + layout_y;
752 //Don't draw characters completely off-screen.
753 if(gy <= (vdbl ? -32 : -16) || gy >= sheight)
754 break;
755 if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= swidth)
756 break;
757 //Compute the bounding box.
758 uint32_t xstart = 0;
759 uint32_t ystart = 0;
760 uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
761 uint32_t ylength = (vdbl ? 32 : 16);
762 if(gx < 0) xstart = -gx;
763 if(gy < 0) ystart = -gy;
764 xlength -= xstart;
765 ylength -= ystart;
766 if(gx + xlength > swidth) xlength = swidth - gx;
767 if(gy + ylength > sheight) ylength = sheight - gy;
768 if(g.data)
769 for(size_t i = 0; i < ylength; i++) {
770 typename framebuffer<X>::element_t* r = scr.rowptr(gy + ystart + i) +
771 (gx + xstart);
772 uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0);
773 uint32_t d = g.data[_y >> (g.wide ? 1 : 2)];
774 if(g.wide)
775 d >>= 16 - ((_y & 1) << 4);
776 else
777 d >>= 24 - ((_y & 3) << 3);
778 if(hdbl)
779 for(size_t j = 0; j < xlength; j++) {
780 uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1);
781 if(((d >> b) & 1) != 0)
782 fg.apply(r[j]);
783 else
784 bg.apply(r[j]);
786 else
787 for(size_t j = 0; j < xlength; j++) {
788 uint32_t b = (g.wide ? 15 : 7) - (j + xstart);
789 if(((d >> b) & 1) != 0)
790 fg.apply(r[j]);
791 else
792 bg.apply(r[j]);
795 else
796 for(size_t i = 0; i < ylength; i++) {
797 typename framebuffer<X>::element_t* r = scr.rowptr(gy + ystart + i) +
798 (gx + xstart);
799 for(size_t j = 0; j < xlength; j++)
800 bg.apply(r[j]);
802 layout_x += (hdbl ? 2 : 1) * (g.wide ? 16 : 8);
807 void premultiplied_color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift, bool X) throw()
809 if(X) {
810 uint64_t r = ((orig >> 16) & 0xFF) * 257;
811 uint64_t g = ((orig >> 8) & 0xFF) * 257;
812 uint64_t b = (orig & 0xFF) * 257;
813 uint64_t color = (r << rshift) | (g << gshift) | (b << bshift);
814 hiHI = color & 0xFFFF0000FFFFULL;
815 loHI = (color & 0xFFFF0000FFFF0000ULL) >> 16;
816 hiHI *= (static_cast<uint32_t>(origa) * 256);
817 loHI *= (static_cast<uint32_t>(origa) * 256);
818 } else {
819 uint32_t r = (orig >> 16) & 0xFF;
820 uint32_t g = (orig >> 8) & 0xFF;
821 uint32_t b = orig & 0xFF;
822 uint32_t color = (r << rshift) | (g << gshift) | (b << bshift);
823 hi = color & 0xFF00FF;
824 lo = (color & 0xFF00FF00) >> 8;
825 hi *= origa;
826 lo *= origa;
830 template class framebuffer<false>;
831 template class framebuffer<true>;
832 template void render_queue::run(struct framebuffer<false>&);
833 template void render_queue::run(struct framebuffer<true>&);
834 template void bitmap_font::render(struct framebuffer<false>& scr, int32_t x, int32_t y, const std::string& text,
835 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw();
836 template void bitmap_font::render(struct framebuffer<true>& scr, int32_t x, int32_t y, const std::string& text,
837 premultiplied_color fg, premultiplied_color bg, bool hdbl, bool vdbl) throw();