簡述:將帶透明通道的PNG/BMP圖片疊加到Framebuf上

PNG圖片,維基百科簡介:https://zh.wikipedia.org/wiki/PNG
現在帶透明通道的最常見的PNG文件都是PNG 32格式,所以首先將PNG文件解析爲RGBA-8-8-8-8即32位真彩像素(PNG 32)(A代表Alpha透明通道)

BMP 幀解析

BMP是最簡單的圖片文件格式,基本上就是爲了存放在文件而勉強加了一些頭尾而已。
看維基百科 https://en.wikipedia.org/wiki/BMP_file_format 的幀格式表,然後寫結構體去序列化反序列化就能解出RGBA像素了。
以前用過c語言解析BMP,但是忘記寫博客了,後面又寫了一篇RUST解析BMP的分析文章:https://www.cnblogs.com/yucloud/p/rust-bmp_learn.html

PNG 幀解析

PNG文件編碼比BMP複雜一點,但還是可以解析的。在no_std rust上我就偷懶使用了minipng庫。
如果你需要c語言,可以自己寫一個,參考標準文檔RFC2083即可
需要Rust語言的解析器,可以參考:https://github.com/pommicket/minipng/blob/trunk/src/lib.rs

文件幀主要是單層TLV(Type-Length-Value)的方式。

RFC 2083 總結來說就是:

Header:文件格式頭/也叫文件署名域,用於標識是否爲PNG文件,避免了與其他格式的衝突和誤判。
一個文件裏有格式頭,還有多個數據塊。
每個數據塊都由幾個元素構成:長度,類型,數據體,CRC。這個基本所有的協議和文件都有類似設計。

PNG 定義了兩種類型的數據塊(chunk),一種是標準的關鍵數據塊(critical chunk),另一種可選的輔助數據塊(ancillary chunks)。我們只需要標準的關鍵數據塊。

這裏找到一篇表格化的幀解析文章:https://blog.csdn.net/qq_60131542/article/details/123450382

壓縮算法:LZ77,哈夫曼huffman,Quantization

以下數據塊I字開頭是Image的意思

  • IHDR,這個是圖片頭塊,包括了圖片信息:寬高、位深、顏色類型、壓縮算法、濾波器方法、隔行掃描方法。
  • PLTE 調色板區塊(可選)
  • IDAT 圖片數據塊,像素RGBA就在這裏。該區有多個塊
  • IEND 圖片結束塊,也是特定的幾個字節。
    還有其他的輔助區,但是一般我們用不到。總之TLV的方式可以讓你通過 Type區分+Len偏移 只去拿需要的數據,而不用管其他區。

總之,RGBA從PNG文件解出來即可。

疊加算法

需要注意的是,透明通道A在屏幕上是不存在的,它唯一的作用是用於和背景運算疊加。也就是說,A通道是需要有背景色才能運算的。

這是將屏幕的RGB像素與現在PNG的RGBA像素合併/疊加的算法:
屏幕只能顯示RGB,所有的A通道都會通過計算後疊加到RGB各分量上面

    fn blend_rgba_to_rgb(&self, background: &mut BltPixel) {
        // Alpha Blending
        // Result = Foreground×α + Background×(1−α)
        let alpha = self.0[3] as f32 / 255.0;
        let r = self.0[0] as f32;
        let g = self.0[1] as f32;
        let b = self.0[2] as f32;

        let blended_r = ((1.0 - alpha) * background.red as f32 + alpha * r) as u8;
        let blended_g = ((1.0 - alpha) * background.green as f32 + alpha * g) as u8;
        let blended_b = ((1.0 - alpha) * background.blue as f32 + alpha * b) as u8;

        background.red = blended_r;
        background.green = blended_g;
        background.blue = blended_b;
    }

以下是矩形疊加

///數學公式解法
//設:需要將png渲染到屏幕framebuf上矩形mfb上
//設:png的圖片大小爲(size_xp,size_yp)
//當前正在疊加渲染的座標爲 (x,y)
                for y in 0..size_yp {
                    for x in 0..size_xp {
                        vec_png[x + y * size_xp].blend_blt_pixel(&mut mfb[x + y * size_xp]);
                    }
                }

我們把從PNG文件裏解出來的RGBA像素通過矩形疊加到屏幕framebuf的像素上,即可完成PNG的顯示。
如果沒辦法獲得矩形部分的mfb(mframebuf),也可以直接獲取全屏framebuf再通過計算 需要渲染的起點座標偏移(x0,y0),完成矩形部分疊加。

問題:這有什麼用
答案:您看到的鼠標就是這樣疊加上去的,另外還有顯示器按鍵菜單等等OSD(on-screen display)功能,底層都是這個原理。

這篇文章是我爲Slint-UI圖像界面庫貢獻了一個UEFI的鼠標協議支持後寫的一些記錄,PR詳見:https://github.com/slint-ui/slint/pull/5084/files

總結:最流行的文件/文件系統/分區格式/網絡協議,基本上幀格式都是類似的 HDR(長度類型Checksum) Payload(多個數據塊)END。結合一些魔法(TLV,算法指示,狀態機指示)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章