2 #include <snes/snes.hpp>
11 typedef uint8_t uint8
;
12 typedef uint16_t uint16
;
13 typedef uint32_t uint32
;
15 typedef int16_t int16
;
16 typedef int32_t int32
;
17 #include <nall/platform.hpp>
18 #include <nall/endian.hpp>
19 #include <nall/varint.hpp>
20 #include <nall/bit.hpp>
21 #include <nall/serializer.hpp>
22 #include <nall/property.hpp>
24 #include <ui-libsnes/libsnes.hpp>
28 extern uint32_t fontdata
[];
32 inline uint32_t blend(uint32_t orig
, uint16_t ialpha
, uint32_t pl
, uint32_t ph
) throw()
34 const uint32_t X
= 0xFF00FFU
;
35 const uint32_t Y
= 0xFF00FF00U
;
36 return ((ialpha
* ((orig
>> 8) & X
) + ph
) & Y
) | (((ialpha
* (orig
& X
) + pl
)
40 //This is Jenkin's MIX function.
41 uint32_t keyhash(uint32_t key
, uint32_t item
, uint32_t mod
) throw()
46 a
=a
-b
; a
=a
-c
; a
=a
^(c
>> 13);
47 b
=b
-c
; b
=b
-a
; b
=b
^(a
<< 8);
48 c
=c
-a
; c
=c
-b
; c
=c
^(b
>> 13);
49 a
=a
-b
; a
=a
-c
; a
=a
^(c
>> 12);
50 b
=b
-c
; b
=b
-a
; b
=b
^(a
<< 16);
51 c
=c
-a
; c
=c
-b
; c
=c
^(b
>> 5);
52 a
=a
-b
; a
=a
-c
; a
=a
^(c
>> 3);
53 b
=b
-c
; b
=b
-a
; b
=b
^(a
<< 10);
54 c
=c
-a
; c
=c
-b
; c
=c
^(b
>> 15);
59 //Locate glyph in font. Returns <width, offset> pair. Zero offset should be interpretted as an empty
61 std::pair
<uint32_t, size_t> find_glyph(uint32_t codepoint
, int32_t x
, int32_t y
, int32_t orig_x
,
62 int32_t& next_x
, int32_t& next_y
) throw()
66 cwidth
= 64 - (x
- orig_x
) % 64;
69 return std::make_pair(cwidth
, 0);
70 } else if(codepoint
== 10) {
73 return std::make_pair(0, 0);
74 } else if(codepoint
== 32) {
77 return std::make_pair(8, 0);
79 uint32_t mdir
= fontdata
[0];
80 uint32_t mseed
= fontdata
[mdir
];
81 uint32_t msize
= fontdata
[mdir
+ 1];
82 uint32_t midx
= keyhash(mseed
, codepoint
, msize
);
83 uint32_t sdir
= fontdata
[mdir
+ 2 + midx
];
84 if(!fontdata
[sdir
+ 1]) {
85 //Character not found.
88 return std::make_pair(8, 0);
90 uint32_t sseed
= fontdata
[sdir
];
91 uint32_t ssize
= fontdata
[sdir
+ 1];
92 uint32_t sidx
= keyhash(sseed
, codepoint
, ssize
);
93 if(fontdata
[sdir
+ 2 + 2 * sidx
] != codepoint
) {
94 //Character not found.
97 return std::make_pair(8, 0);
99 bool wide
= (fontdata
[fontdata
[sdir
+ 2 + 2 * sidx
+ 1]] != 0);
100 next_x
= x
+ (wide
? 16 : 8);
102 return std::make_pair(wide
? 16 : 8, fontdata
[sdir
+ 2 + 2 * sidx
+ 1] + 1);
106 render_object::~render_object() throw()
110 void render_text(struct screen
& scr
, int32_t x
, int32_t y
, const std::string
& text
, uint32_t fg
,
111 uint16_t fgalpha
, uint32_t bg
, uint16_t bgalpha
) throw(std::bad_alloc
)
113 uint32_t pfgl
= (fg
& 0xFF00FF) * fgalpha
;
114 uint32_t pfgh
= ((fg
>> 8) & 0xFF00FF) * fgalpha
;
115 uint32_t pbgl
= (bg
& 0xFF00FF) * bgalpha
;
116 uint32_t pbgh
= ((bg
>> 8) & 0xFF00FF) * bgalpha
;
117 uint16_t ifga
= 256 - fgalpha
;
118 uint16_t ibga
= 256 - bgalpha
;
120 uint32_t unicode_code
= 0;
121 uint8_t unicode_left
= 0;
122 for(size_t i
= 0; i
< text
.length(); i
++) {
123 uint8_t ch
= text
[i
];
125 unicode_code
= text
[i
];
129 unicode_code
= 64 * unicode_code
+ ch
- 128;
132 } else if(ch
< 224) {
133 unicode_code
= ch
- 192;
136 } else if(ch
< 240) {
137 unicode_code
= ch
- 224;
140 } else if(ch
< 248) {
141 unicode_code
= ch
- 240;
146 int32_t next_x
, next_y
;
147 auto p
= find_glyph(unicode_code
, x
, y
, orig_x
, next_x
, next_y
);
149 uint32_t dw
= p
.first
;
152 uint32_t cx
= static_cast<uint32_t>(static_cast<int32_t>(scr
.originx
) + x
);
153 uint32_t cy
= static_cast<uint32_t>(static_cast<int32_t>(scr
.originy
) + y
);
154 while(cx
> scr
.width
&& dw
> 0) {
159 while(cy
> scr
.height
&& dh
> 0) {
164 while(cx
+ dw
> scr
.width
&& dw
> 0)
166 while(cy
+ dh
> scr
.height
&& dh
> 0)
169 continue; //Outside screen.
173 for(uint32_t j
= 0; j
< dh
; j
++) {
174 uint32_t* base
= scr
.rowptr(cy
+ j
) + cx
;
175 for(uint32_t i
= 0; i
< dw
; i
++)
176 base
[i
] = blend(base
[i
], ibga
, pbgl
, pbgh
);
180 for(uint32_t j
= 0; j
< dh
; j
++) {
181 uint32_t dataword
= fontdata
[p
.second
+ (dy
+ j
) / (32 / p
.first
)];
182 uint32_t* base
= scr
.rowptr(cy
+ j
) + cx
;
183 for(uint32_t i
= 0; i
< dw
; i
++)
184 if(((dataword
>> (31 - ((dy
+ j
) % (32 / p
.first
)) * p
.first
- (dx
+ i
))) & 1))
185 base
[i
] = blend(base
[i
], ifga
, pfgl
, pfgh
);
187 base
[i
] = blend(base
[i
], ibga
, pbgl
, pbgh
);
195 void render_queue::add(struct render_object
& obj
) throw(std::bad_alloc
)
200 void render_queue::run(struct screen
& scr
) throw()
202 for(auto i
= q
.begin(); i
!= q
.end(); i
++) {
212 void render_queue::clear() throw()
214 for(auto i
= q
.begin(); i
!= q
.end(); i
++)
219 render_queue::~render_queue() throw()
224 uint32_t screen::make_color(uint8_t r
, uint8_t g
, uint8_t b
) throw()
226 return (static_cast<uint32_t>(r
) << active_rshift
) |
227 (static_cast<uint32_t>(g
) << active_gshift
) |
228 (static_cast<uint32_t>(b
) << active_bshift
);
231 lcscreen::lcscreen(const uint16_t* mem
, bool hires
, bool interlace
, bool overscan
, bool region
) throw()
233 uint32_t dataoffset
= 0;
234 width
= hires
? 512 : 256;
239 dataoffset
= overscan
? 9 : 1;
243 dataoffset
= overscan
? 16 : 9;
247 memory
= mem
+ dataoffset
* 1024;
248 pitch
= interlace
? 512 : 1024;
252 lcscreen::lcscreen(const uint16_t* mem
, uint32_t _width
, uint32_t _height
) throw()
261 lcscreen::lcscreen() throw()
271 lcscreen::lcscreen(const lcscreen
& ls
) throw(std::bad_alloc
)
277 allocated
= static_cast<size_t>(width
) * height
;
278 memory
= new uint16_t[allocated
];
279 for(size_t l
= 0; l
< height
; l
++)
280 memcpy(const_cast<uint16_t*>(memory
+ l
* width
), ls
.memory
+ l
* ls
.pitch
, 2 * width
);
283 lcscreen
& lcscreen::operator=(const lcscreen
& ls
) throw(std::bad_alloc
, std::runtime_error
)
286 throw std::runtime_error("Can't copy to non-user memory");
289 if(allocated
< static_cast<size_t>(ls
.width
) * ls
.height
) {
290 size_t p_allocated
= static_cast<size_t>(ls
.width
) * ls
.height
;
291 memory
= new uint16_t[p_allocated
];
292 allocated
= p_allocated
;
297 for(size_t l
= 0; l
< height
; l
++)
298 memcpy(const_cast<uint16_t*>(memory
+ l
* width
), ls
.memory
+ l
* ls
.pitch
, 2 * width
);
302 lcscreen::~lcscreen()
305 delete[] const_cast<uint16_t*>(memory
);
308 void lcscreen::load(const std::vector
<char>& data
) throw(std::bad_alloc
, std::runtime_error
)
311 throw std::runtime_error("Can't load to non-user memory");
312 const uint8_t* data2
= reinterpret_cast<const uint8_t*>(&data
[0]);
314 throw std::runtime_error("Corrupt saved screenshot data");
315 uint32_t _width
= static_cast<uint32_t>(data2
[0]) * 256 + static_cast<uint32_t>(data2
[1]);
316 if(_width
> 1 && data
.size() % (2 * _width
) != 2)
317 throw std::runtime_error("Corrupt saved screenshot data");
318 uint32_t _height
= (data
.size() - 2) / (2 * _width
);
319 if(allocated
< static_cast<size_t>(_width
) * _height
) {
320 size_t p_allocated
= static_cast<size_t>(_width
) * _height
;
321 memory
= new uint16_t[p_allocated
];
322 allocated
= p_allocated
;
324 uint16_t* mem
= const_cast<uint16_t*>(memory
);
328 for(size_t i
= 0; i
< (data
.size() - 2) / 2; i
++)
329 mem
[i
] = static_cast<uint16_t>(data2
[2 + 2 * i
]) * 256 +
330 static_cast<uint16_t>(data2
[2 + 2 * i
+ 1]);
333 void lcscreen::save(std::vector
<char>& data
) throw(std::bad_alloc
)
335 data
.resize(2 + 2 * static_cast<size_t>(width
) * height
);
336 uint8_t* data2
= reinterpret_cast<uint8_t*>(&data
[0]);
337 data2
[0] = (width
>> 8);
339 for(size_t i
= 0; i
< (data
.size() - 2) / 2; i
++) {
340 data
[2 + 2 * i
] = memory
[(i
/ width
) * pitch
+ (i
% width
)] >> 8;
341 data
[2 + 2 * i
+ 1] = memory
[(i
/ width
) * pitch
+ (i
% width
)];
345 void lcscreen::save_png(const std::string
& file
) throw(std::bad_alloc
, std::runtime_error
)
347 unsigned char clevels
[32];
348 for(unsigned i
= 0; i
< 32; i
++)
349 clevels
[i
] = 255 * i
/ 31;
350 uint8_t* buffer
= new uint8_t[3 * static_cast<size_t>(width
) * height
];
351 for(uint32_t j
= 0; j
< height
; j
++)
352 for(uint32_t i
= 0; i
< width
; i
++) {
353 uint16_t word
= memory
[pitch
* j
+ i
];
354 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 0] = clevels
[(word
>> 10) & 0x1F];
355 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 1] = clevels
[(word
>> 5) & 0x1F];
356 buffer
[3 * static_cast<size_t>(width
) * j
+ 3 * i
+ 2] = clevels
[(word
) & 0x1F];
359 save_png_data(file
, buffer
, width
, height
);
367 void screen::copy_from(lcscreen
& scr
, uint32_t hscale
, uint32_t vscale
) throw()
369 uint32_t copyable_width
= (width
- originx
) / hscale
;
370 uint32_t copyable_height
= (height
- originy
) / vscale
;
371 copyable_width
= (copyable_width
> scr
.width
) ? scr
.width
: copyable_width
;
372 copyable_height
= (copyable_height
> scr
.height
) ? scr
.height
: copyable_height
;
373 for(uint32_t y
= 0; y
< height
; y
++) {
374 memset(rowptr(y
), 0, 4 * width
);
376 for(uint32_t y
= 0; y
< copyable_height
; y
++) {
377 uint32_t line
= y
* vscale
+ originy
;
378 uint32_t* ptr
= rowptr(line
) + originx
;
379 const uint16_t* sbase
= scr
.memory
+ y
* scr
.pitch
;
380 for(uint32_t x
= 0; x
< copyable_width
; x
++) {
381 uint32_t c
= palette
[sbase
[x
] % 32768];
382 for(uint32_t i
= 0; i
< hscale
; i
++)
385 for(uint32_t j
= 1; j
< vscale
; j
++)
386 memcpy(rowptr(line
+ j
) + originx
, rowptr(line
) + originx
, 4 * hscale
* copyable_width
);
390 void screen::reallocate(uint32_t _width
, uint32_t _height
, uint32_t _originx
, uint32_t _originy
, bool upside_down
)
391 throw(std::bad_alloc
)
393 if(_width
== width
&& _height
== height
) {
398 if(!_width
|| !_height
) {
399 width
= height
= originx
= originy
= pitch
= 0;
400 if(memory
&& !user_memory
)
404 flipped
= upside_down
;
407 uint32_t* newmem
= new uint32_t[_width
* _height
];
413 if(memory
&& !user_memory
)
417 flipped
= upside_down
;
420 void screen::set(uint32_t* _memory
, uint32_t _width
, uint32_t _height
, uint32_t _originx
, uint32_t _originy
,
421 uint32_t _pitch
) throw()
423 if(memory
&& !user_memory
)
435 uint32_t* screen::rowptr(uint32_t row
) throw()
438 row
= height
- row
- 1;
439 return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(memory
) + row
* pitch
);
442 screen::screen() throw()
444 uint32_t _magic
= 403703808;
445 uint8_t* magic
= reinterpret_cast<uint8_t*>(&_magic
);
447 width
= height
= originx
= originy
= pitch
= 0;
450 active_rshift
= active_gshift
= active_bshift
= 255;
451 set_palette(magic
[0], magic
[1], magic
[2]);
454 screen::~screen() throw()
456 if(memory
&& !user_memory
)
460 void screen::set_palette(uint32_t rshift
, uint32_t gshift
, uint32_t bshift
) throw()
462 if(rshift
== active_rshift
&& gshift
== active_gshift
&& bshift
== active_bshift
)
464 uint32_t old_rshift
= active_rshift
;
465 uint32_t old_gshift
= active_gshift
;
466 uint32_t old_bshift
= active_bshift
;
467 uint32_t xpalette
[32];
468 for(unsigned i
= 0; i
< 32; i
++)
469 xpalette
[i
] = (i
* 255 / 31);
470 for(unsigned i
= 0; i
< 32768; i
++) {
471 palette
[i
] = (xpalette
[(i
>> 10) & 31] << rshift
) +
472 (xpalette
[(i
>> 5) & 31] << gshift
) +
473 (xpalette
[i
& 31] << bshift
);
475 active_rshift
= rshift
;
476 active_gshift
= gshift
;
477 active_bshift
= bshift
;
479 for(uint32_t j
= 0; j
< height
; j
++) {
480 uint32_t* rp
= rowptr(j
);
481 for(uint32_t i
= 0; i
< width
; i
++) {
483 uint32_t r
= (x
>> old_rshift
) & 0xFF;
484 uint32_t g
= (x
>> old_gshift
) & 0xFF;
485 uint32_t b
= (x
>> old_bshift
) & 0xFF;
486 x
= (r
<< active_rshift
) | (g
<< active_gshift
) | (b
<< active_bshift
);