前段时间写了个操作系统,取名叫 LemonCore,惯例,播放一下 BadApple。
- OS:github.com/13m0n4de/lemon-core
- App: gist.github.com/13m0n4de/238b13f361e0326a0e8868e00119fb2a
- Video: [Touhou] Bad Apple!! PV [Shadow]
预览效果:
视频取摸
直接解析视频需要实现一个解析视频文件格式的程序,有点太夸张,有点没必要。BadApple 只有黑白两色,可以分别用二进制位 1 和 0 存储。并且每帧的图像长宽都是一样的,直接将数据连续存放也没有问题(事先和解析程序商议好视频长宽的话)。
例如:
## ## ## 11011011 # ### ## 10111011 # ## # # --> 10110101 ## # ## 11010011 ## # ### 11010111
其实也就相当于 LCD 显示屏的图片取摸过程,只是更加简单一些,不用考虑彩色情况,也不用多出个将视频帧切分为图片的步骤。
转换脚本:
import cv2 import numpy as np cap = cv2.VideoCapture("bad_apple.mp4") width = 480 height = 360 binary_flat = [] while True: ret, frame = cap.read() if not ret: break frame = cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA) gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray_frame, 128, 1, cv2.THRESH_BINARY) binary_flat.append(binary.flatten()) packed_bytes = np.packbits(binary_flat) packed_bytes.tofile("bad_apple.bin") cap.release()
可以得到 \(480 \times 360 \times 6569 \div 8 = 141890400\) 字节的文件。
显示程序
用户程序库移植了 embedded-graphics crate,实现起来要简单不少。
先实现一个使用 BinaryColor 色彩的显示设备,显示像素时只需根据色彩状态 (On & Off) 将 FrameBuffer 对应数据写成 0xffffff 或 0x000000:
impl Display { fn new(size: Size) -> Self { let fb_ptr = framebuffer() as *mut u8; let fb = unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN) }; Self { size, fb } } fn draw_pixel(&mut self, x: i32, y: i32, color: BinaryColor) { let idx = (y * VIRTGPU_XRES as i32 + x) as usize * 4; if idx + 2 < self.fb.len() { let c = if color.is_on() { 0xff } else { 0x00 }; self.fb[idx] = c; self.fb[idx + 1] = c; self.fb[idx + 2] = c; } } } impl DrawTarget for Display { type Color = BinaryColor; type Error = core::convert::Infallible; fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> where I: IntoIterator<Item = Pixel<Self::Color>>, { for Pixel(Point { x, y }, color) in pixels { self.draw_pixel(x, y, color); } framebuffer_flush(); Ok(()) } } impl OriginDimensions for Display { fn size(&self) -> Size { self.size } }
显示时,不断从 /data/bad_apple.bin 中读取每一帧的数据,用其构建黑白图像 ImageRaw::<BinaryColor> 并显示在屏幕中心:
#[no_mangle] extern "Rust" fn main() -> i32 { let mut display = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); display.clear(BinaryColor::On).unwrap(); let fd = open("/data/bad_apple.bin", OpenFlags::RDONLY); assert!(fd >= 0); let fd = fd as usize; let mut image_buffer = vec![0u8; IMAGE_BUFFER_SIZE]; for _ in 0..FRAMES_COUNT { if read(fd, &mut image_buffer) == 0 { break; } let raw_image = ImageRaw::<BinaryColor>::new(&image_buffer, IMAGE_WIDTH as u32); let image = Image::new( &raw_image, Point { x: (VIRTGPU_XRES as i32 - IMAGE_WIDTH as i32) / 2, y: (VIRTGPU_YRES as i32 - IMAGE_HEIGHT as i32) / 2, }, ); image.draw(&mut display).unwrap(); } close(fd); display.clear(BinaryColor::On).unwrap(); 0 }
嗯结束了。
答辩的时候还短暂地展示了一下,老师当然是一脸迷惑。