2 #include <snes/snes.hpp>
4 #include "core/misc.hpp"
5 #include "core/png.hpp"
6 #include "core/render.hpp"
16 #define TAG_ZEROWIDTH 0
19 #define TAG_TABULATION 3
20 #define TAG_WIDTH_MASK 3
21 #define TAG_LINECHANGE 4
23 extern const char* font_hex_data
;
27 std::vector
<uint32_t> font_glyph_data
;
28 std::map
<uint32_t, uint32_t> font_glyph_offsets
;
30 uint32_t parse_word(const char* x
)
35 unsigned long v
= strtoul(buf
, &end
, 16);
38 //std::cerr << "Parse word " << buf << std::endl;
44 static bool iflag
= false;
48 font_glyph_data
.resize(7);
50 font_glyph_data
[0] = TAG_NARROW
;
51 font_glyph_data
[1] = 0;
52 font_glyph_data
[2] = 0;
53 font_glyph_data
[3] = 0;
54 font_glyph_data
[4] = 0;
56 font_glyph_data
[5] = TAG_TABULATION
;
58 font_glyph_data
[6] = TAG_ZEROWIDTH
| TAG_LINECHANGE
;
62 for(size_t i
= 0;; i
++) {
64 switch(font_hex_data
[i
]) {
67 //Skip spaces at start of line.
77 if(lsptr
== i
|| font_hex_data
[lsptr
] == '#')
79 cp
= strtoul(font_hex_data
+ lsptr
, &end
, 16);
81 messages
<< "Malformed line " << lc
<< " in font data" << std::endl
;
84 fdatastart
= end
- font_hex_data
+ 1;
85 if(i
- fdatastart
== 32) {
87 font_glyph_offsets
[cp
] = font_glyph_data
.size();
88 font_glyph_data
.push_back(TAG_NARROW
);
89 for(uint32_t k
= 0; k
< 4; k
++)
90 font_glyph_data
.push_back(parse_word(end
+ 1 + 8 * k
));
91 } else if(i
- fdatastart
== 64) {
93 font_glyph_offsets
[cp
] = font_glyph_data
.size();
94 font_glyph_data
.push_back(TAG_WIDE
);
95 for(uint32_t k
= 0; k
< 8; k
++)
96 font_glyph_data
.push_back(parse_word(end
+ 1 + 8 * k
));
98 messages
<< "Malformed line " << lc
<< " in font data" << std::endl
;
102 if(font_hex_data
[i
] != '\r' || font_hex_data
[i
+ 1] != '\n')
107 if(!font_hex_data
[i
])
111 //Special characters.
112 font_glyph_offsets
[9] = 5;
113 font_glyph_offsets
[10] = 6;
114 font_glyph_offsets
[32] = 0;
117 uint32_t glyphs_narrow
= 0;
118 uint32_t glyphs_wide
= 0;
119 uint32_t glyphs_special
= 0;
120 for(auto i
: font_glyph_offsets
) {
121 if(font_glyph_data
[i
.second
] == TAG_NARROW
)
123 else if(font_glyph_data
[i
.second
] == TAG_WIDE
)
129 messages
<< "Loaded font data: " << glyphs
<< " glyphs (" << glyphs_narrow
<< " narrow, " <<
130 glyphs_wide
<< " wide, " << glyphs_special
<< " special)." << std::endl
;
134 inline uint32_t find_font_glyph_offset(uint32_t cp
)
136 return font_glyph_offsets
.count(cp
) ? font_glyph_offsets
[cp
] : 0;
139 inline uint32_t process_tag(uint32_t tag
, int32_t& x
, int32_t& y
, int32_t orig_x
, bool hdbl
, bool vdbl
)
142 switch(tag
& TAG_WIDTH_MASK
) {
153 dwidth
= 0x40 - (x
& 0x3F);
156 x
+= dwidth
* (hdbl
? 2 : 1);
157 if(tag
& TAG_LINECHANGE
) {
158 y
+= 16 * (vdbl
? 2 : 1);
164 inline bool is_visible(uint32_t tag
)
166 return ((tag
& TAG_WIDTH_MASK
) == TAG_NARROW
|| (tag
& TAG_WIDTH_MASK
) == TAG_WIDE
);
176 std::pair
<uint32_t, const uint32_t*> find_glyph(uint32_t codepoint
, int32_t x
, int32_t y
, int32_t orig_x
,
177 int32_t& next_x
, int32_t& next_y
, bool hdbl
, bool vdbl
) throw()
182 uint32_t offset
= find_font_glyph_offset(codepoint
);
183 uint32_t tag
= font_glyph_data
[offset
];
184 uint32_t dwidth
= process_tag(tag
, next_x
, next_y
, orig_x
, hdbl
, vdbl
);
185 bool visible
= is_visible(tag
);
186 return std::pair
<uint32_t, const uint32_t*>(dwidth
, visible
? &font_glyph_data
[offset
+ 1] : NULL
);
189 render_object::~render_object() throw()
193 void render_text(struct screen
& scr
, int32_t x
, int32_t y
, const std::string
& text
, premultiplied_color fg
,
194 premultiplied_color bg
, bool hdbl
, bool vdbl
) throw(std::bad_alloc
)
197 uint32_t unicode_code
= 0;
198 uint8_t unicode_left
= 0;
199 for(size_t i
= 0; i
< text
.length(); i
++) {
200 uint8_t ch
= text
[i
];
202 unicode_code
= text
[i
];
206 unicode_code
= 64 * unicode_code
+ ch
- 128;
209 } else if(ch
< 224) {
210 unicode_code
= ch
- 192;
213 } else if(ch
< 240) {
214 unicode_code
= ch
- 224;
217 } else if(ch
< 248) {
218 unicode_code
= ch
- 240;
223 int32_t next_x
, next_y
;
224 auto p
= find_glyph(unicode_code
, x
, y
, orig_x
, next_x
, next_y
, hdbl
, vdbl
);
226 uint32_t dw
= p
.first
* (hdbl
? 2 : 1);
228 uint32_t dh
= 16 * (vdbl
? 2 : 1);
229 uint32_t cx
= static_cast<uint32_t>(static_cast<int32_t>(scr
.originx
) + x
);
230 uint32_t cy
= static_cast<uint32_t>(static_cast<int32_t>(scr
.originy
) + y
);
231 while(cx
> scr
.width
&& dw
> 0) {
236 while(cy
> scr
.height
&& dh
> 0) {
241 while(cx
+ dw
> scr
.width
&& dw
> 0)
243 while(cy
+ dh
> scr
.height
&& dh
> 0)
246 continue; //Outside screen.
248 uint32_t rshift
= (p
.first
== 16) ? (vdbl
? 2 : 1) : (vdbl
? 3 : 2);
249 uint32_t rishift
= (p
.first
== 16) ? 4 : 3;
250 uint32_t xshift
= hdbl
? 1 : 0;
251 uint32_t yshift
= vdbl
? 1 : 0;
254 if(p
.second
== NULL
) {
256 for(uint32_t j
= 0; j
< dh
; j
++) {
257 uint32_t* base
= scr
.rowptr(cy
+ j
) + cx
;
258 for(uint32_t i
= 0; i
< dw
; i
++)
262 for(uint32_t j
= 0; j
< dh
; j
++) {
263 uint32_t dataword
= p
.second
[(dy
+ j
) >> rshift
];
264 uint32_t* base
= scr
.rowptr(cy
+ j
) + cx
;
265 uint32_t rbit
= (~((dy
+ j
) >> yshift
<< rishift
) & 31) - (dx
>> xshift
);
267 for(uint32_t i
= 0; i
< dw
; i
++)
268 if((dataword
>> (rbit
- ((i
+ b
) >> 1))) & 1)
273 for(uint32_t i
= 0; i
< dw
; i
++)
274 if((dataword
>> (rbit
- i
)) & 1)
286 void render_queue::add(struct render_object
& obj
) throw(std::bad_alloc
)
288 struct node
* n
= reinterpret_cast<struct node
*>(alloc(sizeof(node
)));
292 queue_tail
= queue_tail
->next
= n
;
294 queue_head
= queue_tail
= n
;
297 void render_queue::run(struct screen
& scr
) throw()
299 struct node
* tmp
= queue_head
;
309 void render_queue::clear() throw()
312 queue_head
->obj
->~render_object();
313 queue_head
= queue_head
->next
;
315 //Release all memory for reuse.
316 memory_allocated
= 0;
321 void* render_queue::alloc(size_t block
) throw(std::bad_alloc
)
323 block
= (block
+ 15) / 16 * 16;
324 if(block
> RENDER_PAGE_SIZE
)
325 throw std::bad_alloc();
326 if(pages
== 0 || memory_allocated
+ block
> pages
* RENDER_PAGE_SIZE
) {
327 memory_allocated
= pages
* RENDER_PAGE_SIZE
;
330 void* mem
= memory
[memory_allocated
/ RENDER_PAGE_SIZE
].content
+ (memory_allocated
% RENDER_PAGE_SIZE
);
331 memory_allocated
+= block
;
335 render_queue::render_queue() throw()
339 memory_allocated
= 0;
343 render_queue::~render_queue() throw()
349 struct render_object* queue_head;
350 size_t memory_allocated;
351 struct page { char content[RENDER_PAGE_SIZE]; };
352 std::map<size_t, page> memory;
355 uint32_t screen::make_color(uint8_t r
, uint8_t g
, uint8_t b
) throw()
360 return (_r
<< 16) + (_g
<< 8) + _b
;
363 lcscreen::lcscreen(const uint32_t* mem
, bool hires
, bool interlace
, bool overscan
, bool region
) throw()
365 uint32_t dataoffset
= 0;
366 width
= hires
? 512 : 256;
371 dataoffset
= overscan
? 9 : 1;
375 dataoffset
= overscan
? 16 : 9;
379 memory
= mem
+ dataoffset
* 1024;
380 pitch
= interlace
? 512 : 1024;
384 lcscreen::lcscreen(const uint32_t* mem
, uint32_t _width
, uint32_t _height
) throw()
393 lcscreen::lcscreen() throw()
403 lcscreen::lcscreen(const lcscreen
& ls
) throw(std::bad_alloc
)
409 allocated
= static_cast<size_t>(width
) * height
;
410 memory
= new uint32_t[allocated
];
411 for(size_t l
= 0; l
< height
; l
++)
412 memcpy(const_cast<uint32_t*>(memory
+ l
* width
), ls
.memory
+ l
* ls
.pitch
, 4 * width
);
415 lcscreen
& lcscreen::operator=(const lcscreen
& ls
) throw(std::bad_alloc
, std::runtime_error
)
418 throw std::runtime_error("Can't copy to non-user memory");
421 if(allocated
< static_cast<size_t>(ls
.width
) * ls
.height
) {
422 size_t p_allocated
= static_cast<size_t>(ls
.width
) * ls
.height
;
423 memory
= new uint32_t[p_allocated
];
424 allocated
= p_allocated
;
429 for(size_t l
= 0; l
< height
; l
++)
430 memcpy(const_cast<uint32_t*>(memory
+ l
* width
), ls
.memory
+ l
* ls
.pitch
, 4 * width
);
434 lcscreen::~lcscreen()
437 delete[] const_cast<uint32_t*>(memory
);
440 void lcscreen::load(const std::vector
<char>& data
) throw(std::bad_alloc
, std::runtime_error
)
443 throw std::runtime_error("Can't load to non-user memory");
444 const uint8_t* data2
= reinterpret_cast<const uint8_t*>(&data
[0]);
446 throw std::runtime_error("Corrupt saved screenshot data");
447 uint32_t _width
= static_cast<uint32_t>(data2
[0]) * 256 + static_cast<uint32_t>(data2
[1]);
448 if(_width
> 1 && data
.size() % (3 * _width
) != 2)
449 throw std::runtime_error("Corrupt saved screenshot data");
450 uint32_t _height
= (data
.size() - 2) / (3 * _width
);
451 if(allocated
< static_cast<size_t>(_width
) * _height
) {
452 size_t p_allocated
= static_cast<size_t>(_width
) * _height
;
453 memory
= new uint32_t[p_allocated
];
454 allocated
= p_allocated
;
456 uint32_t* mem
= const_cast<uint32_t*>(memory
);
460 for(size_t i
= 0; i
< (data
.size() - 2) / 3; i
++)
461 mem
[i
] = static_cast<uint32_t>(data2
[2 + 3 * i
]) * 65536 +
462 static_cast<uint32_t>(data2
[2 + 3 * i
+ 1]) * 256 +
463 static_cast<uint32_t>(data2
[2 + 3 * i
+ 2]);
466 void lcscreen::save(std::vector
<char>& data
) throw(std::bad_alloc
)
468 data
.resize(2 + 3 * static_cast<size_t>(width
) * height
);
469 uint8_t* data2
= reinterpret_cast<uint8_t*>(&data
[0]);
470 data2
[0] = (width
>> 8);
472 for(size_t i
= 0; i
< (data
.size() - 2) / 3; i
++) {
473 data
[2 + 3 * i
] = memory
[(i
/ width
) * pitch
+ (i
% width
)] >> 16;
474 data
[2 + 3 * i
+ 1] = memory
[(i
/ width
) * pitch
+ (i
% width
)] >> 8;
475 data
[2 + 3 * i
+ 2] = memory
[(i
/ width
) * pitch
+ (i
% width
)];
479 void lcscreen::save_png(const std::string
& file
) throw(std::bad_alloc
, std::runtime_error
)
481 uint8_t* buffer
= new uint8_t[3 * static_cast<size_t>(width
) * height
];
482 for(uint32_t j
= 0; j
< height
; j
++)
483 for(uint32_t i
= 0; i
< width
; i
++) {
484 uint32_t word
= memory
[pitch
* j
+ i
];
485 uint32_t l
= 1 + ((word
>> 15) & 0xF);
486 uint32_t r
= l
* ((word
>> 0) & 0x1F);
487 uint32_t g
= l
* ((word
>> 5) & 0x1F);
488 uint32_t b
= l
* ((word
>> 10) & 0x1F);
489 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 0] = r
* 255 / 496;
490 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 1] = g
* 255 / 496;
491 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 2] = b
* 255 / 496;
494 save_png_data(file
, buffer
, width
, height
);
502 void screen::copy_from(lcscreen
& scr
, uint32_t hscale
, uint32_t vscale
) throw()
504 if(width
< originx
|| height
< originy
) {
505 //Just clear the screen.
506 for(uint32_t y
= 0; y
< height
; y
++)
507 memset(rowptr(y
), 0, 4 * width
);
510 uint32_t copyable_width
= 0, copyable_height
= 0;
512 copyable_width
= (width
- originx
) / hscale
;
514 copyable_height
= (height
- originy
) / vscale
;
515 copyable_width
= (copyable_width
> scr
.width
) ? scr
.width
: copyable_width
;
516 copyable_height
= (copyable_height
> scr
.height
) ? scr
.height
: copyable_height
;
517 for(uint32_t y
= 0; y
< height
; y
++)
518 memset(rowptr(y
), 0, 4 * width
);
519 for(uint32_t y
= 0; y
< copyable_height
; y
++) {
520 uint32_t line
= y
* vscale
+ originy
;
521 uint32_t* ptr
= rowptr(line
) + originx
;
522 const uint32_t* sbase
= scr
.memory
+ y
* scr
.pitch
;
523 for(uint32_t x
= 0; x
< copyable_width
; x
++) {
524 uint32_t c
= palette
[sbase
[x
] & 0x7FFFF];
525 for(uint32_t i
= 0; i
< hscale
; i
++)
528 for(uint32_t j
= 1; j
< vscale
; j
++)
529 memcpy(rowptr(line
+ j
) + originx
, rowptr(line
) + originx
, 4 * hscale
* copyable_width
);
533 void screen::reallocate(uint32_t _width
, uint32_t _height
, bool upside_down
) throw(std::bad_alloc
)
535 if(_width
== width
&& _height
== height
)
537 if(!_width
|| !_height
) {
538 width
= height
= originx
= originy
= pitch
= 0;
539 if(memory
&& !user_memory
)
543 flipped
= upside_down
;
546 uint32_t* newmem
= new uint32_t[_width
* _height
];
550 if(memory
&& !user_memory
)
554 flipped
= upside_down
;
557 void screen::set(uint32_t* _memory
, uint32_t _width
, uint32_t _height
, uint32_t _pitch
) throw()
559 if(memory
&& !user_memory
)
569 void screen::set_origin(uint32_t _originx
, uint32_t _originy
) throw()
576 uint32_t* screen::rowptr(uint32_t row
) throw()
579 row
= height
- row
- 1;
580 return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(memory
) + row
* pitch
);
583 screen::screen() throw()
586 width
= height
= originx
= originy
= pitch
= 0;
590 set_palette(16, 8, 0);
593 screen::~screen() throw()
595 if(memory
&& !user_memory
)
600 void clip_range(uint32_t origin
, uint32_t size
, int32_t base
, int32_t& minc
, int32_t& maxc
) throw()
602 int64_t _origin
= origin
;
603 int64_t _size
= size
;
604 int64_t _base
= base
;
605 int64_t _minc
= minc
;
606 int64_t _maxc
= maxc
;
607 int64_t mincoordinate
= _base
+ _origin
+ _minc
;
608 int64_t maxcoordinate
= _base
+ _origin
+ _maxc
;
609 if(mincoordinate
< 0)
610 _minc
= _minc
- mincoordinate
;
611 if(maxcoordinate
> _size
)
612 _maxc
= _maxc
- (maxcoordinate
- _size
);
622 void screen::set_palette(uint32_t r
, uint32_t g
, uint32_t b
)
625 palette
= new uint32_t[0x80000];
626 else if(r
== palette_r
&& g
== palette_g
&& b
== palette_b
)
628 for(size_t i
= 0; i
< static_cast<size_t>(width
) * height
; i
++) {
629 uint32_t word
= memory
[i
];
630 uint32_t R
= (word
>> palette_r
) & 0xFF;
631 uint32_t G
= (word
>> palette_g
) & 0xFF;
632 uint32_t B
= (word
>> palette_b
) & 0xFF;
633 memory
[i
] = (R
<< r
) | (G
<< g
) | (B
<< b
);
635 for(unsigned i
= 0; i
< 0x80000; i
++) {
636 unsigned l
= 1 + ((i
>> 15) & 0xF);
637 unsigned R
= (i
>> 0) & 0x1F;
638 unsigned G
= (i
>> 5) & 0x1F;
639 unsigned B
= (i
>> 10) & 0x1F;
640 double _l
= static_cast<double>(l
);
641 double m
= 255.0 / 496.0;
642 R
= floor(m
* R
* _l
+ 0.5);
643 G
= floor(m
* G
* _l
+ 0.5);
644 B
= floor(m
* B
* _l
+ 0.5);
645 palette
[i
] = (R
<< r
) | (G
<< g
) | (B
<< b
);
652 void premultiplied_color::set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
654 uint32_t r
= (orig
>> 16) & 0xFF;
655 uint32_t g
= (orig
>> 8) & 0xFF;
656 uint32_t b
= orig
& 0xFF;
657 uint32_t color
= (r
<< rshift
) | (g
<< gshift
) | (b
<< bshift
);
658 hi
= color
& 0xFF00FF;
659 lo
= (color
& 0xFF00FF00) >> 8;