1 #include "framebuffer.hpp"
4 #include "serialization.hpp"
13 #define SCREENSHOT_RGB_MAGIC 0x74212536U
17 unsigned default_shift_r
;
18 unsigned default_shift_g
;
19 unsigned default_shift_b
;
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
;
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)
44 for(size_t i
= 0; i
< srcsize
; i
++)
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]);
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];
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]);
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
)
79 for(size_t i
= 0; i
< elts
; i
++)
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
];
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];
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
];
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
114 std::function
<void(int64_t& v
)> fn
;
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
;
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
++)
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
;
154 addr
= info
.mem
+ pixel_offset
;
157 height
= info
.height
;
158 stride
= info
.stride
;
162 raw::raw() throw(std::bad_alloc
)
173 raw::raw(const raw
& f
) throw(std::bad_alloc
)
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
)
190 throw std::runtime_error("Target framebuffer is not writable");
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
];
199 allocated
= newallocated
;
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
);
216 void raw::load(const std::vector
<char>& data
) throw(std::bad_alloc
, std::runtime_error
)
219 throw std::runtime_error("Bad screenshot data");
221 throw std::runtime_error("Target framebuffer is not writable");
223 const uint8_t* data2
= reinterpret_cast<const uint8_t*>(&data
[0]);
224 size_t legacy_width
= serialization::u16b(data2
);
229 if(legacy_width
> 0 && data
.size() % (3 * legacy_width
) == 2) {
231 for(pixfmt
* f
: pixfmts())
232 if(f
->get_magic() == 0)
235 throw std::runtime_error("Unknown screenshot format");
236 _width
= legacy_width
;
237 _height
= (data
.size() - 2) / (3 * legacy_width
);
242 throw std::runtime_error("Bad screenshot data");
244 uint32_t magic
= serialization::u32b(data2
+ 2);
245 for(pixfmt
* f
: pixfmts())
246 if(f
->get_magic() == magic
)
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
];
264 allocated
= newalloc
;
269 stride
= _width
* bpp
;
271 decode_words
<1>(reinterpret_cast<uint8_t*>(addr
), data2
+ dataoffset
, data
.size() - dataoffset
);
273 decode_words
<2>(reinterpret_cast<uint8_t*>(addr
), data2
+ dataoffset
, data
.size() - dataoffset
);
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
);
286 size_t bpp
= fmt
->get_bpp();
287 size_t sbpp
= fmt
->get_ss_bpp();
290 uint32_t magic
= fmt
->get_magic();
293 //Save in legacy format.
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
);
300 //Choose the first two bytes so that screenshot is bad in legacy format.
302 while((sbpp
* width
* height
+ 8) % (3 * m
) == 2)
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
);
311 data2
= reinterpret_cast<uint8_t*>(&data
[0]);
312 for(size_t i
= 0; i
< height
; i
++) {
314 encode_words
<1>(data2
+ offset
+ sbpp
* width
* i
, memory
+ stride
* i
, width
);
316 encode_words
<2>(data2
+ offset
+ sbpp
* width
* i
, memory
+ stride
* i
, width
);
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
);
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
);
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
; }
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);
376 #define DECBUF_SIZE 4096
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
;
386 for(size_t y
= 0; y
< height
; y
++)
387 memset(rowptr(y
), 0, sizeof(typename fb
<X
>::element_t
) * width
);
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.
402 size_t copyable_width
= 0, copyable_height
= 0;
404 copyable_width
= (width
- offset_x
) / hscale
;
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();
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
];
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
);
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
)
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
);
455 void fb
<X
>::set(element_t
* _memory
, size_t _width
, size_t _height
, size_t _pitch
) throw()
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
) {
473 element_t
* newmem
= new element_t
[ustride
* _height
+ 4];
477 mem
= new element_t
[ustride
* _height
+ 4];
479 memset(mem
, 0, sizeof(element_t
) * ustride
* _height
);
483 upside_down
= _upside_down
;
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
;
495 typename fb
<X
>::element_t
* fb
<X
>::rowptr(size_t row
) throw()
498 row
= height
- row
- 1;
499 uint32_t align
= (16 - reinterpret_cast<size_t>(mem
)) % 16 / 4;
500 return mem
+ stride
* row
+ align
;
504 const typename fb
<X
>::element_t
* fb
<X
>::rowptr(size_t row
) const throw()
507 row
= height
- row
- 1;
508 return mem
+ stride
* row
;
511 template<bool X
> uint8_t fb
<X
>::get_palette_r() const throw() { return auxpal
.rshift
; }
512 template<bool X
> uint8_t fb
<X
>::get_palette_g() const throw() { return auxpal
.gshift
; }
513 template<bool X
> uint8_t fb
<X
>::get_palette_b() const throw() { return auxpal
.bshift
; }
515 size_t raw::get_width() const throw() { return width
; }
516 size_t raw::get_height() const throw() { return height
; }
517 template<bool X
> size_t fb
<X
>::get_origin_x() const throw() { return offset_x
; }
518 template<bool X
> size_t fb
<X
>::get_origin_y() const throw() { return offset_y
; }
520 void queue::add(struct object
& obj
) throw(std::bad_alloc
)
522 struct node
* n
= reinterpret_cast<struct node
*>(alloc(sizeof(node
)));
527 queue_tail
= queue_tail
->next
= n
;
529 queue_head
= queue_tail
= n
;
532 void queue::copy_from(queue
& q
) throw(std::bad_alloc
)
534 struct node
* tmp
= q
.queue_head
;
537 tmp
->obj
->clone(*this);
544 template<bool X
> void queue::run(struct fb
<X
>& scr
) throw()
546 struct node
* tmp
= queue_head
;
557 void queue::clear() throw()
560 if(!queue_head
->killed
)
561 queue_head
->obj
->~object();
562 queue_head
= queue_head
->next
;
564 //Release all memory for reuse.
565 memory_allocated
= 0;
570 void* queue::alloc(size_t block
) throw(std::bad_alloc
)
572 block
= (block
+ 15) / 16 * 16;
573 if(block
> RENDER_PAGE_SIZE
)
574 throw std::bad_alloc();
575 if(pages
== 0 || memory_allocated
+ block
> pages
* RENDER_PAGE_SIZE
) {
576 memory_allocated
= pages
* RENDER_PAGE_SIZE
;
579 void* mem
= memory
[memory_allocated
/ RENDER_PAGE_SIZE
].content
+ (memory_allocated
% RENDER_PAGE_SIZE
);
580 memory_allocated
+= block
;
584 void queue::kill_request(void* obj
) throw()
586 struct node
* tmp
= queue_head
;
589 if(!tmp
->killed
&& tmp
->obj
->kill_request(obj
)) {
600 queue::queue() throw()
604 memory_allocated
= 0;
608 queue::~queue() throw()
613 object::object() throw()
617 object::~object() throw()
621 bool object::kill_request_ifeq(void* myobj
, void* killobj
)
630 bool object::kill_request(void* obj
) throw()
635 font::font() throw(std::bad_alloc
)
637 bad_glyph_data
[0] = 0x018001AAU
;
638 bad_glyph_data
[1] = 0x01800180U
;
639 bad_glyph_data
[2] = 0x01800180U
;
640 bad_glyph_data
[3] = 0x55800180U
;
641 bad_glyph
.wide
= false;
642 bad_glyph
.data
= bad_glyph_data
;
645 void font::load_hex_glyph(const char* data
, size_t size
) throw(std::bad_alloc
, std::runtime_error
)
648 std::string
line(data
, data
+ size
);
649 size_t linelen
= line
.length();
651 //Check if the line is valid.
652 //Line format is <hex digits>:<32 or 64 hex digits>.
653 size_t splitter
= line
.find(':');
654 if(splitter
>= linelen
|| !(splitter
+ 33 == linelen
|| splitter
+ 65 == linelen
))
655 (stringfmt() << "Invalid line '" << line
<< "'").throwex(); //No :, or not 32/64 hexes after.
656 if(line
.find_first_not_of("0123456789ABCDEFabcdef:") < line
.length())
657 (stringfmt() << "Invalid line '" << line
<< "'").throwex(); //Invalid character.
658 if(line
.find(':', splitter
+ 1) < line
.length())
659 (stringfmt() << "Invalid line '" << line
<< "'").throwex(); //Second :.
661 (stringfmt() << "Invalid line '" << line
<< "'").throwex(); //Too long codepoint.
663 std::string codepoint
= line
.substr(0, splitter
);
664 std::string cdata
= line
.substr(splitter
+ 1);
665 strcpy(buf2
, codepoint
.c_str());
667 unsigned long cp
= strtoul(buf2
, &end2
, 16);
668 if(*end2
|| cp
> 0x10FFFF)
669 (stringfmt() << "Invalid line '" << line
<< "'").throwex(); //Codepoint out of range.
670 glyphs
[cp
].wide
= (cdata
.length() == 64);
671 size_t p
= memory
.size();
672 for(size_t i
= 0; i
< cdata
.length(); i
+= 8) {
675 for(size_t j
= 0; j
< 8; j
++)
676 buf
[j
] = cdata
[i
+ j
];
677 memory
.push_back(strtoul(buf
, &end
, 16));
679 glyphs
[cp
].offset
= p
;
682 void font::load_hex(const char* data
, size_t size
) throw(std::bad_alloc
, std::runtime_error
)
684 const char* enddata
= data
+ size
;
685 while(data
!= enddata
) {
687 while(data
+ linesize
!= enddata
&& data
[linesize
] != '\n' && data
[linesize
] != '\r')
689 if(linesize
&& data
[0] != '#')
690 load_hex_glyph(data
, linesize
);
699 glyphs
[32].wide
= false;
700 glyphs
[32].offset
= memory
.size() - 4;
701 for(auto& i
: glyphs
)
702 i
.second
.data
= &memory
[i
.second
.offset
];
705 const font::glyph
& font::get_glyph(uint32_t glyph
) throw()
707 if(glyphs
.count(glyph
))
708 return glyphs
[glyph
];
713 std::set
<uint32_t> font::get_glyphs_set()
715 std::set
<uint32_t> out
;
716 for(auto& i
: glyphs
)
721 const font::glyph
& font::get_bad_glyph() throw()
726 std::pair
<size_t, size_t> font::get_metrics(const std::string
& string
) throw()
728 size_t commit_width
= 0;
729 size_t commit_height
= 0;
730 size_t linelength
= 0;
731 utf8::to32i(string
.begin(), string
.end(), lambda_output_iterator
<int32_t>([this, &linelength
, &commit_width
,
732 &commit_height
](const int32_t& cp
) -> void {
733 const glyph
& g
= get_glyph(cp
);
736 linelength
= (linelength
+ TABSTOPS
) / TABSTOPS
* TABSTOPS
;
737 commit_width
= max(commit_width
, linelength
);
743 linelength
= linelength
+ (g
.wide
? 16 : 8);
744 commit_width
= max(commit_width
, linelength
);
748 return std::make_pair(commit_width
, commit_height
);
751 std::vector
<font::layout
> font::dolayout(const std::string
& string
) throw(std::bad_alloc
)
753 //First, calculate the number of glyphs to draw.
755 utf8::to32i(string
.begin(), string
.end(), lambda_output_iterator
<int32_t>([&chars
](const int32_t& cp
)
757 if(cp
!= 9 && cp
!= 10)
761 std::vector
<layout
> l
;
766 utf8::to32i(string
.begin(), string
.end(), lambda_output_iterator
<int32_t>([this, &layout_x
, &layout_y
,
767 &l
, >r
](const int32_t cp
) {
768 const glyph
& g
= get_glyph(cp
);
771 layout_x
= (layout_x
+ TABSTOPS
) / TABSTOPS
* TABSTOPS
;
775 layout_y
= layout_y
+ 16;
780 l
[gtr
++].dglyph
= &g
;
781 layout_x
= layout_x
+ (g
.wide
? 16 : 8);;
787 template<bool X
> void font::render(struct fb
<X
>& scr
, int32_t x
, int32_t y
, const std::string
& text
,
788 color fg
, color bg
, bool hdbl
, bool vdbl
) throw()
790 x
+= scr
.get_origin_x();
791 y
+= scr
.get_origin_y();
794 size_t swidth
= scr
.get_width();
795 size_t sheight
= scr
.get_height();
796 utf8::to32i(text
.begin(), text
.end(), lambda_output_iterator
<int32_t>([this, x
, y
, &scr
, &layout_x
,
797 &layout_y
, swidth
, sheight
, hdbl
, vdbl
, &fg
, &bg
](const int32_t& cp
) {
798 const glyph
& g
= get_glyph(cp
);
801 layout_x
= (layout_x
+ TABSTOPS
) / TABSTOPS
* TABSTOPS
;
805 layout_y
= layout_y
+ (vdbl
? 32 : 16);
808 //Render this glyph at x + layout_x, y + layout_y.
809 int32_t gx
= x
+ layout_x
;
810 int32_t gy
= y
+ layout_y
;
811 //Don't draw characters completely off-screen.
812 if(gy
<= (vdbl
? -32 : -16) || gy
>= (ssize_t
)sheight
)
814 if(gx
<= -(hdbl
? 2 : 1) * (g
.wide
? 16 : 8) || gx
>= (ssize_t
)swidth
)
816 //Compute the bounding box.
819 uint32_t xlength
= (hdbl
? 2 : 1) * (g
.wide
? 16 : 8);
820 uint32_t ylength
= (vdbl
? 32 : 16);
821 if(gx
< 0) xstart
= -gx
;
822 if(gy
< 0) ystart
= -gy
;
825 if(gx
+ xstart
+ xlength
> swidth
) xlength
= swidth
- (gx
+ xstart
);
826 if(gy
+ ystart
+ ylength
> sheight
) ylength
= sheight
- (gy
+ ystart
);
828 for(size_t i
= 0; i
< ylength
; i
++) {
829 typename fb
<X
>::element_t
* r
= scr
.rowptr(gy
+ ystart
+ i
) +
831 uint32_t _y
= (i
+ ystart
) >> (vdbl
? 1 : 0);
832 uint32_t d
= g
.data
[_y
>> (g
.wide
? 1 : 2)];
834 d
>>= 16 - ((_y
& 1) << 4);
836 d
>>= 24 - ((_y
& 3) << 3);
838 for(size_t j
= 0; j
< xlength
; j
++) {
839 uint32_t b
= (g
.wide
? 15 : 7) - ((j
+ xstart
) >> 1);
840 if(((d
>> b
) & 1) != 0)
846 for(size_t j
= 0; j
< xlength
; j
++) {
847 uint32_t b
= (g
.wide
? 15 : 7) - (j
+ xstart
);
848 if(((d
>> b
) & 1) != 0)
855 for(size_t i
= 0; i
< ylength
; i
++) {
856 typename fb
<X
>::element_t
* r
= scr
.rowptr(gy
+ ystart
+ i
) +
858 for(size_t j
= 0; j
< xlength
; j
++)
861 layout_x
+= (hdbl
? 2 : 1) * (g
.wide
? 16 : 8);
866 color::color(const std::string
& clr
) throw(std::bad_alloc
, std::runtime_error
)
870 auto& cspecs
= colornames();
871 for(auto& t
: token_iterator
<char>::foreach(clr
, {" ","\t"}, true)) {
872 if(t
.length() > 0 && t
[0] == '#') {
874 throw std::runtime_error("Base color (" + t
+ ") can't be used as modifier");
876 col
= hex::from
<uint32_t>(_t
.substr(1));
881 throw std::runtime_error("Invalid color (modifier) '" + t
+ "'");
882 if(!first
&& !cspecs
[t
].second
)
883 throw std::runtime_error("Base color (" + t
+ ") can't be used as modifier");
884 if(first
&& cspecs
[t
].second
)
885 throw std::runtime_error("Modifier (" + t
+ ") can't be used as base color");
886 (cspecs
[t
].first
)(col
);
892 std::string
color::stringify(int64_t number
)
894 for(auto& i
: colornames()) {
898 (i
.second
.first
)(col
);
903 return "transparent";
904 else if(number
< 16777216)
905 return "#" + hex::to
<uint32_t>(number
).substr(2);
907 return "#" + hex::to
<uint32_t>(number
);
910 void color::set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
, bool X
) throw()
913 uint64_t r
= ((orig
>> 16) & 0xFF) * 257;
914 uint64_t g
= ((orig
>> 8) & 0xFF) * 257;
915 uint64_t b
= (orig
& 0xFF) * 257;
917 uint64_t fullc
= ~0ULL & ~((a
<< rshift
) | (a
<< gshift
) | (a
<< bshift
));
918 uint64_t color
= (r
<< rshift
) | (g
<< gshift
) | (b
<< bshift
) | fullc
;
919 hiHI
= color
& 0xFFFF0000FFFFULL
;
920 loHI
= (color
& 0xFFFF0000FFFF0000ULL
) >> 16;
921 hiHI
*= (static_cast<uint32_t>(origa
) * 256);
922 loHI
*= (static_cast<uint32_t>(origa
) * 256);
924 uint32_t r
= (orig
>> 16) & 0xFF;
925 uint32_t g
= (orig
>> 8) & 0xFF;
926 uint32_t b
= orig
& 0xFF;
928 uint64_t fullc
= ~0UL & ~((a
<< rshift
) | (a
<< gshift
) | (a
<< bshift
));
929 uint32_t color
= (r
<< rshift
) | (g
<< gshift
) | (b
<< bshift
) | fullc
;
930 hi
= color
& 0xFF00FF;
931 lo
= (color
& 0xFF00FF00) >> 8;
939 void adjust_hmM_hue(int16_t& hue
, int16_t& m
, int16_t& M
, double adj
)
944 hue
= (hue
+ static_cast<uint32_t>(adj
* S
)) % (6 * S
);
946 void adjust_ls_saturation(double& s
, double& l
, double adj
)
948 s
= clip(s
+ adj
, 0.0, 1.0);
950 void adjust_ls_lightness(double& s
, double& l
, double adj
)
952 l
= clip(l
+ adj
, 0.0, 1.0);
954 template<void(*adjustfn
)(double& s
, double& l
, double adj
)>
955 void adjust_hmM_sl(int16_t& hue
, int16_t& m
, int16_t& M
, double adj
)
958 double _m
= m
/ 255.0;
959 double _M
= M
/ 255.0;
960 double l
= (_m
+ _M
) / 2;
962 if(l
== 0 || l
== 1) s
= 0;
963 else if(l
<= 0.5) s
= _M
/ l
- 1;
964 else s
= (_M
- l
) / (1 - l
);
966 if(l
<= 0.5) _M
= l
* (s
+ 1);
967 else _M
= l
+ s
- l
* s
;
972 hue
= S1
? (S2
* hue
/ S1
) : 0;
978 const uint8_t hsl2rgb_flags
[] = {24, 52, 6, 13, 33, 19};
979 template<void(*adjustfn
)(int16_t& hue
, int16_t& m
, int16_t& M
, double adj
)>
980 uint32_t adjustcolor(uint32_t color
, double shift
)
982 int16_t R
= (color
>> 16) & 0xFF;
983 int16_t G
= (color
>> 8) & 0xFF;
984 int16_t B
= color
& 0xFF;
985 int16_t m
= min(R
, min(G
, B
));
986 int16_t M
= max(R
, max(G
, B
));
990 hue
= G
- B
+ 6 * S1
;
992 hue
= B
- R
+ 2 * S1
;
994 hue
= R
- G
+ 4 * S1
;
995 adjustfn(hue
, m
, M
, shift
);
997 return ((uint32_t)m
<< 16) | ((uint32_t)m
<< 8) | (uint32_t)m
;
1003 V
[2] = m
+ hue
% S2
;
1004 V
[3] = M
- hue
% S2
;
1005 uint8_t flag
= hsl2rgb_flags
[hue
/ S2
];
1006 return (V
[(flag
>> 4) & 3] << 16) | (V
[(flag
>> 2) & 3] << 8) | (V
[flag
& 3]);
1010 int64_t color_rotate_hue(int64_t basecolor
, int step
, int steps
)
1013 throw std::runtime_error("Expected nonzero steps for hue rotation");
1015 //Special: Any rotation of transparent is transparent.
1018 uint32_t asteps
= std::abs(steps
);
1020 step
= asteps
- step
% asteps
; //Reverse order.
1021 double hueshift
= 6.0 * (step
% asteps
) / asteps
;
1022 basecolor
= adjustcolor
<adjust_hmM_hue
>(basecolor
& 0xFFFFFF, hueshift
) | (basecolor
& 0xFF000000);
1026 int64_t color_adjust_saturation(int64_t color
, double adjust
)
1028 if(color
< 0) return color
;
1029 return adjustcolor
<adjust_hmM_sl
<adjust_ls_saturation
>>(color
& 0xFFFFFF, adjust
) | (color
& 0xFF000000);
1032 int64_t color_adjust_lightness(int64_t color
, double adjust
)
1034 if(color
< 0) return color
;
1035 return adjustcolor
<adjust_hmM_sl
<adjust_ls_lightness
>>(color
& 0xFFFFFF, adjust
) | (color
& 0xFF000000);
1038 template class fb
<false>;
1039 template class fb
<true>;
1040 template void queue::run(struct fb
<false>&);
1041 template void queue::run(struct fb
<true>&);
1042 template void font::render(struct fb
<false>& scr
, int32_t x
, int32_t y
, const std::string
& text
,
1043 color fg
, color bg
, bool hdbl
, bool vdbl
) throw();
1044 template void font::render(struct fb
<true>& scr
, int32_t x
, int32_t y
, const std::string
& text
,
1045 color fg
, color bg
, bool hdbl
, bool vdbl
) throw();