马里奥01

目标

实现对Nes文件中图像的提取(上)

工具

调试所需的浏览器使用FirefoxDeveloperEdition,因为有跨域问题,只有火狐开发者版本可以。

原理

红白机的图像技术的核心是所谓的 tile 和 block。所谓 tile 就是一块 8x8 像素大小的区域,一个 block 就是 16x16 大小的区域。多个以上的结构对齐,就产生了我们看到的图像。

详见https://zhuanlan.zhihu.com/p/34144965

一个Tile 8x8 像素

每个像素2 bits

所以一个Tile 16 bytes

将所有资源以这种形式提取出来显示,即可以找到需要的图像

实现

每页显示8X8个Tiles,将每个像素都扩大十倍。设置画布设置为640X640

通过ajax拿到数据,实现drawNes方法

drawNes

drawNes方法负责画满canvas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const drawNes = bytes => {
let canvas = e("#id-canvas")
let context = canvas.getContext('2d')
let tileNum = 8//一页8x8个tiles
let tileSize = 8//一个tile有8x8个像素
let pixelwidth = 10//每个像素的宽度
let numberOfBytesPerTile = 16//16个字节一个图块
for (let i = 0; i < tileNum; i++) {
for (let j = 0; j < tileNum; j++) {
//算出bytes
let x = j * tileSize * pixelwidth
let y = i * tileSize * pixelwidth
let index = window.offset + (i * tileNum + j) * numberOfBytesPerTile
//其实应该drawTile更准确
drawTile(context, bytes.slice(index), x, y, pixelwidth)
}
}
}

drawTile

draw每个tile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const drawTile = (context, data, x, y, pixelwidth) =>{
const colors = [
'white',
'#fe1000',
'#ffb010',
'#aa3030',
]
let w = pixelwidth
let h = pixelwidth
for (let i = 0; i < 8; i++) {
let p1 = data[i]
let p2 = data[i + 8]
for (let j = 0; j < 8; j++) {
//8 bit per line
//在j循环中每一次画一个像素点
//78 69
//0100 1110 0100 0101
let c1 = (p1 >> (7 - j)) & 0b00000001
let c2 = (p2 >> (7 - j)) & 0b00000001
let pixel = (c2 << 1) + c1
// if (pixel == 0){
// continue
// }
let color = colors[pixel]
context.fillStyle = color
let px = x + j * w
let py = y + i * h
context.fillRect(px, py, w, h)
}
}
}

绑定点击事件

1
2
3
4
5
6
<div class="gua-controls">
<button data-action="change_offset" data-offset="-1024">-1024</button>
<button data-action="change_offset" data-offset="-16">-16</button>
<button data-action="change_offset" data-offset="16">16</button>
<button data-action="change_offset" data-offset="1024">1024</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const actions = {
change_offset(offset) {
window.offset += offset
e('h3').innerHTML = window.offset
drawNes(window.bytes)
},
}
const bindEvent = () => {
e('.gua-controls').addEventListener('click', event => {
let action = event.target.dataset.action
let offset = Number(event.target.dataset.offset)
actions[action] && actions[action](offset)
})
}

通过表的方式来替代if语句