跳转至

Bad Apple!! on LemonCore

前段时间写了个操作系统,取名叫 LemonCore,惯例,播放一下 BadApple

预览效果:

视频取摸

直接解析视频需要实现一个解析视频文件格式的程序,有点太夸张,有点没必要。BadApple 只有黑白两色,可以分别用二进制位 10 存储。并且每帧的图像长宽都是一样的,直接将数据连续存放也没有问题(事先和解析程序商议好视频长宽的话

例如:

## ## ##            11011011
# ### ##            10111011
# ## # #    -->     10110101 
## #  ##            11010011
## # ###            11010111

其实也就相当于 LCD 显示屏的图片取摸过程,只是更加简单一些,不用考虑彩色情况,也不用多出个将视频帧切分为图片的步骤。

转换脚本:

converter.py
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 对应数据写成 0xffffff0x000000

bad_apple.rs
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> 并显示在屏幕中心:

bad_apple.rs
#[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
}

嗯结束了。

答辩的时候还短暂地展示了一下,老师当然是一脸迷惑。