1 #include "framebuffer.hpp"
4 #include "serialization.hpp"
13 #define SCREENSHOT_RGB_MAGIC 0x74212536U
17 const char* render_page_id
= "Render queues";
18 unsigned default_shift_r
;
19 unsigned default_shift_g
;
20 unsigned default_shift_b
;
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
;
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)
45 for(size_t i
= 0; i
< srcsize
; i
++)
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]);
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];
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]);
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
)
80 for(size_t i
= 0; i
< elts
; i
++)
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
];
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];
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
];
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
115 std::function
<void(int64_t& v
)> fn
;
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
;
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
++)
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
;
155 addr
= info
.mem
+ pixel_offset
;
158 height
= info
.height
;
159 stride
= info
.stride
;
163 raw::raw() throw(std::bad_alloc
)
174 raw::raw(const raw
& f
) throw(std::bad_alloc
)
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
)
191 throw std::runtime_error("Target framebuffer is not writable");
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
];
200 allocated
= newallocated
;
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
);
217 void raw::load(const std::vector
<char>& data
) throw(std::bad_alloc
, std::runtime_error
)
220 throw std::runtime_error("Bad screenshot data");
222 throw std::runtime_error("Target framebuffer is not writable");
224 const uint8_t* data2
= reinterpret_cast<const uint8_t*>(&data
[0]);
225 size_t legacy_width
= serialization::u16b(data2
);
230 if(legacy_width
> 0 && data
.size() % (3 * legacy_width
) == 2) {
232 for(pixfmt
* f
: pixfmts())
233 if(f
->get_magic() == 0)
236 throw std::runtime_error("Unknown screenshot format");
237 _width
= legacy_width
;
238 _height
= (data
.size() - 2) / (3 * legacy_width
);
243 throw std::runtime_error("Bad screenshot data");
245 uint32_t magic
= serialization::u32b(data2
+ 2);
246 for(pixfmt
* f
: pixfmts())
247 if(f
->get_magic() == magic
)
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
];
265 allocated
= newalloc
;
270 stride
= _width
* bpp
;
272 decode_words
<1>(reinterpret_cast<uint8_t*>(addr
), data2
+ dataoffset
, data
.size() - dataoffset
);
274 decode_words
<2>(reinterpret_cast<uint8_t*>(addr
), data2
+ dataoffset
, data
.size() - dataoffset
);
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
);
287 size_t bpp
= fmt
->get_bpp();
288 size_t sbpp
= fmt
->get_ss_bpp();
291 uint32_t magic
= fmt
->get_magic();
294 //Save in legacy format.
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
);
301 //Choose the first two bytes so that screenshot is bad in legacy format.
303 while((sbpp
* width
* height
+ 8) % (3 * m
) == 2)
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
);
312 data2
= reinterpret_cast<uint8_t*>(&data
[0]);
313 for(size_t i
= 0; i
< height
; i
++) {
315 encode_words
<1>(data2
+ offset
+ sbpp
* width
* i
, memory
+ stride
* i
, width
);
317 encode_words
<2>(data2
+ offset
+ sbpp
* width
* i
, memory
+ stride
* i
, width
);
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
);
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
);
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
; }
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);
377 #define DECBUF_SIZE 4096
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
;
387 for(size_t y
= 0; y
< height
; y
++)
388 memset(rowptr(y
), 0, sizeof(typename fb
<X
>::element_t
) * width
);
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.
403 size_t copyable_width
= 0, copyable_height
= 0;
405 copyable_width
= (width
- offset_x
) / hscale
;
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();
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
];
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
);
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
)
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
);
456 void fb
<X
>::set(element_t
* _memory
, size_t _width
, size_t _height
, size_t _pitch
) throw()
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
) {
474 element_t
* newmem
= new element_t
[ustride
* _height
+ 4];
478 mem
= new element_t
[ustride
* _height
+ 4];
480 memset(mem
, 0, sizeof(element_t
) * ustride
* _height
);
484 upside_down
= _upside_down
;
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
;
496 typename fb
<X
>::element_t
* fb
<X
>::rowptr(size_t row
) throw()
499 row
= height
- row
- 1;
500 uint32_t align
= (16 - reinterpret_cast<size_t>(mem
)) % 16 / 4;
501 return mem
+ stride
* row
+ align
;
505 const typename fb
<X
>::element_t
* fb
<X
>::rowptr(size_t row
) const throw()
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
)));
528 queue_tail
= queue_tail
->next
= n
;
530 queue_head
= queue_tail
= n
;
533 void queue::copy_from(queue
& q
) throw(std::bad_alloc
)
535 struct node
* tmp
= q
.queue_head
;
538 tmp
->obj
->clone(*this);
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
;
560 void queue::clear() throw()
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;
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
;
582 void* mem
= memory
[memory_allocated
/ RENDER_PAGE_SIZE
].content
+ (memory_allocated
% RENDER_PAGE_SIZE
);
583 memory_allocated
+= block
;
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
;
594 if(!tmp
->killed
&& tmp
->obj
->kill_request(obj
)) {
605 queue::queue() throw()
606 : tracker(memtracker::singleton(), render_page_id
, sizeof(*this))
610 memory_allocated
= 0;
614 queue::~queue() throw()
619 object::object() throw()
623 object::~object() throw()
627 bool object::kill_request_ifeq(void* myobj
, void* killobj
)
636 bool object::kill_request(void* obj
) throw()
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
)
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 :.
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());
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) {
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
) {
693 while(data
+ linesize
!= enddata
&& data
[linesize
] != '\n' && data
[linesize
] != '\r')
695 if(linesize
&& data
[0] != '#')
696 load_hex_glyph(data
, linesize
);
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
];
719 std::set
<uint32_t> font::get_glyphs_set()
721 std::set
<uint32_t> out
;
722 for(auto& i
: glyphs
)
727 const font::glyph
& font::get_bad_glyph() throw()
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.
750 for_each_glyph(string
, 0, false, false, [&chars
](uint32_t x
, uint32_t y
, const glyph
& g
, bool xdbl
,
755 std::vector
<layout
> l
;
758 for_each_glyph(string
, 0, false, false, [&l
, >r
](uint32_t x
, uint32_t y
, const glyph
& g
, bool xdbl
,
762 l
[gtr
++].dglyph
= &g
;
767 uint32_t font::get_width(const std::string
& string
)
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;
775 width
= (width
+ tabs
) / tabs
* tabs
;
780 width
+= (g
.wide
? 2 : 1);
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.
799 //Don't draw characters completely off-screen.
800 if(gy
<= (vdbl
? -32 : -16) || gy
>= (ssize_t
)sheight
)
802 if(gx
<= -(hdbl
? 2 : 1) * (g
.wide
? 16 : 8) || gx
>= (ssize_t
)swidth
)
804 //Compute the bounding box.
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
;
813 if(gx
+ xstart
+ xlength
> swidth
) xlength
= swidth
- (gx
+ xstart
);
814 if(gy
+ ystart
+ ylength
> sheight
) ylength
= sheight
- (gy
+ ystart
);
816 for(size_t i
= 0; i
< ylength
; i
++) {
817 typename fb
<X
>::element_t
* r
= scr
.rowptr(gy
+ ystart
+ i
) +
819 uint32_t _y
= (i
+ ystart
) >> (vdbl
? 1 : 0);
820 uint32_t d
= g
.data
[_y
>> (g
.wide
? 1 : 2)];
822 d
>>= 16 - ((_y
& 1) << 4);
824 d
>>= 24 - ((_y
& 3) << 3);
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)
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)
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
++)
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);
860 for(size_t i
= 0; i
< height
; i
++) {
862 uint32_t _y
= i
>> (vdbl
? 1 : 0);
863 uint32_t d
= g
.data
[_y
>> (g
.wide
? 1 : 2)];
865 d
>>= 16 - ((_y
& 1) << 4);
867 d
>>= 24 - ((_y
& 3) << 3);
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);
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
;
888 size_t offset
= alignx
;
889 unsigned _xdbl
= xdbl
;
890 unsigned _ydbl
= ydbl
;
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
);
897 layout_x
= (layout_x
+ TABSTOPS
) / TABSTOPS
* TABSTOPS
;
901 layout_y
= layout_y
+ 16;
904 (*_cb
)((layout_x
- offset
) << _xdbl
, layout_y
<< _ydbl
, g
, _xdbl
, _ydbl
);
905 layout_x
= layout_x
+ (g
.wide
? 16 : 8);
911 color::color(const std::string
& clr
) throw(std::bad_alloc
, std::runtime_error
)
915 auto& cspecs
= colornames();
916 for(auto& t
: token_iterator
<char>::foreach(clr
, {" ","\t"}, true)) {
917 if(t
.length() > 0 && t
[0] == '#') {
919 throw std::runtime_error("Base color (" + t
+ ") can't be used as modifier");
921 col
= hex::from
<uint32_t>(_t
.substr(1));
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
);
937 std::string
color::stringify(int64_t number
)
939 for(auto& i
: colornames()) {
943 (i
.second
.first
)(col
);
948 return "transparent";
949 else if(number
< 16777216)
950 return "#" + hex::to
<uint32_t>(number
).substr(2);
952 return "#" + hex::to
<uint32_t>(number
);
955 void color::set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
, bool X
) throw()
958 uint64_t r
= ((orig
>> 16) & 0xFF) * 257;
959 uint64_t g
= ((orig
>> 8) & 0xFF) * 257;
960 uint64_t b
= (orig
& 0xFF) * 257;
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);
969 uint32_t r
= (orig
>> 16) & 0xFF;
970 uint32_t g
= (orig
>> 8) & 0xFF;
971 uint32_t b
= orig
& 0xFF;
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;
984 void adjust_hmM_hue(int16_t& hue
, int16_t& m
, int16_t& M
, double adj
)
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
)
1003 double _m
= m
/ 255.0;
1004 double _M
= M
/ 255.0;
1005 double l
= (_m
+ _M
) / 2;
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
;
1017 hue
= S1
? (S2
* hue
/ S1
) : 0;
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
));
1035 hue
= G
- B
+ 6 * S1
;
1037 hue
= B
- R
+ 2 * S1
;
1039 hue
= R
- G
+ 4 * S1
;
1040 adjustfn(hue
, m
, M
, shift
);
1042 return ((uint32_t)m
<< 16) | ((uint32_t)m
<< 8) | (uint32_t)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
)
1058 throw std::runtime_error("Expected nonzero steps for hue rotation");
1060 //Special: Any rotation of transparent is transparent.
1063 uint32_t asteps
= std::abs(steps
);
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);
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();