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,算法指示,狀態機指示)