终于我们来到了最后,离画出标题 CG 只差一步了。
上一期我们完成了事实上的读取过程,这个时候文件已经全部读取出来了, 然后得到这样一张图:
这个相当于绘图的线稿,后面我们只要按照程序逻辑填充颜色,就可以完成最终的绘图。
我们继续看 fcn_004010f0
的最后一部分,汇编的话是这段:
0x00401253 xor ecx, ecx
0x00401255 mov edx, dword [var_24h_2]
0x00401259 add edx, 0x400 ; 1024
0x0040125f mov eax, 0x3e800
0x00401264 xor ebx, ebx
0x00401266 mov bl, byte [edx]
0x00401268 test ebx, ebx
0x0040126a je 0x40127b
0x0040126c cmp ecx, ebx
0x0040126e jne 0x401277
0x00401270 xor ecx, ecx
0x00401272 mov byte [edx], 0
0x00401275 jmp 0x40127d
0x00401277 mov ecx, ebx
0x00401279 jmp 0x40127d
0x0040127b mov byte [edx], cl
0x0040127d inc edx
0x0040127e dec eax
0x0040127f jne 0x401264
0x00401281 pop ebp
0x00401282 pop edi
0x00401283 pop esi
0x00401284 pop ebx
0x00401285 add esp, 0x10
0x00401288 ret 8
并不是很长,不过反编译的代码更容易理解,我也加好了注释, 注释中提到的「颜色」就是「调色板索引号」,简单起见我们就称之为「颜色」:
ecx = 0; // 填充颜色,初始为零
edx = var_24h_2; // 读取图像内存位置
edx += 0x400; // 跳过调色板
eax = 0x3e800; // 循环次数
do {
ebx = 0; // ebx 高位清零
bl = *(edx); // ebx 低位读出当前颜色
if (ebx != 0) { // 如果读出的颜色不为 0
if (ecx == ebx) { // 如果当前颜色和填充颜色相同
ecx = 0; // 填充颜色设为 0
*(edx) = 0; // 当前位置颜色重写为 0
} else { // 如果当前颜色和填充颜色不同
ecx = ebx; // 填充颜色设定为当前颜色
}
} else { // 如果读出的颜色为 0
*(edx) = cl; // 当前位置颜色重写为填充颜色
}
edx++; // 移到下一个像素位置
eax--; // 循环次数减一
} while (eax != 0); // 循环次数为 0 时终止循环
return eax; // 这个我们不理会
这个循环重复了 0x3e800
次,次数我们应该也熟悉,就是 640x400
。
相比于上一期,这期我们转换为实际代码就太好写了,直接套就好了:
let mut default_color = 0;
for i in 0..image.data.len() {
let current_color = image.data[i];
if current_color != 0 {
if default_color == current_color {
default_color = 0;
image.data[i] = 0;
} else {
default_color = current_color;
}
} else {
image.data[i] = default_color;
}
}
接下来我们就可以画图了,不过敏锐的读者可能会注意到有一个细节我没有提及, 就是图像的坐标系,根据程序读取出的数据,我们画一下就可以知道, 图像的原点是左下角,x 轴方向向右,y 轴向上,所以由于我们的图像库原点是左上角, 所以我们画图时要做一个变换:
for i in 0..self.data.len() {
let x = i % width;
let y = height - 1 - i / width; // 变换 y 轴坐标
let color_index = self.data[i] as usize;
let &(r, g, b) = self.pallete.get_color(color_index).unwrap();
let pixel = image_buffer.get_pixel_mut(x as u32, y as u32);
*pixel = Rgb([r, g, b]);
}
我们已经完整实现了 fcn_004010f0
的全部内容,来画一下我们的成果吧: