圖檔-PNG解析

圖檔-PNG解析
圖檔-PNG解析

舊時『游戲紋理』再用『.bmp格式』, 冇壓缩即使256色盤依然佔用吉間.

『.PNG格式』利LZ77且冇失真壓縮. 且壓縮極高, 配合256色盤,适宜作『游戲紋理』.

數據塊 數值
PNG圖檔標誌 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
圖檔頭 IHDR
调色板 PLTE
图像数据 IDAT
图像结束 IEND

PNG數據塊基本結构.

數據塊結构 size 簡介
LENGTH 整數4字節 數據長度
CHUNK TYPE 整數4字節 類型標記
CHUNK DATA 0~2^32字節 數據
CRC32 整數4字節 CRC32校驗

两字節肆字節數據,冚辦闌用『網络字節』存儲,需轉『主機字節』.

『網络字節』轉『主機字節』
#define  PNG_VALUE32(v)  ((v & 0xff000000) >> 24 | (v & 0x00ff0000) >> 8 | (v & 0x0000ff00) << 8 | (v & 0x000000ff) << 24)
#define  PNG_VALUE16(v)  ((v & 0xff00) >> 8 | (v & 0x00ff) << 8)

游戲引擎需讀PNG 伍個數據塊

PNG數據塊 功能簡介
IHDR 文檔頭
PLTE 色盤
IDAT 圖像數值
IEND 結束
tRNS 圖像透明
bKGD 背景色

PNG圖檔頭捌字節標記,愛蒞識别PNG

0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
137 P N G \r \n 26 \n

對比PNG標識

    if(memcmp(data,  0x0A1A0A0D474E5089, 8) != 0)

return false;// 非png圖檔

CRC32計算必需計『CHUNK TYPE + CHUNK DATA』, 長度『length + 4』而得.

DWORD length = PNG_VALUE32(chunk->length) + 4;
DWORD64 crc = CRC32((PBYTE)(chunk) + 4,length); CRC32計算
DWORD64 crc_ = PNG_VALUE32(chunk->crc_); 轉主機字節
  if(crc_ == crc)

return true;

 

比較crc值

首先定義PNG結构

typedef struct PNG_TAG{  
int   width; 圖宽
int   height; 圖高
int  bitCount; 位圖像素bits 8位,16位,24位,32位
PNG_RGBA       palette[256]; 调色板
PBYTE        buffer; 圖像数据
int               buffer_size; 圖像数据長度
int               length; 临時變量
z_stream stream; ZLIB解壓
} PNG, *PNG_PTR;  

文檔頭結构 IHDR

typedef struct PNG_IHDR_TAG{ 文檔頭 IHDR
int length; Data長度
DWORD type; 標記’IHDR’
DWORD width; 像素寬
DWORD height; 像素高
BYTE  BitDepth; 圖像深度
BYTE  ColorType; 顏色類型
BYTE  Compression; LZ77派生演算法 ,壹定係0
BYTE  Filter; 濾波,止值定0. 事實『0,1,2,3,4』伍種濾波
BYTE  Interlace; 隔行掃描0=冇掃描,1=(Adam7 interlace)
DWORD CRC32; CRC32校驗
}PNG_IHDR,*PNG_IHDR_PTR;  

PNG支緩伍款顏色類型

ColorType 顏色類型 像素
0=Greyscale 灰度圖像 1,2,4,8,16bit
2=Truecolour 真彩色圖像 8,16bit
3=Indexed-colour 索引彩色圖像 1,2,4,8bit
4=Greyscale with alpha 帶α通道資料灰度圖像 8,16bit
6=Truecolour with alpha 帶α通道資料真彩色圖像 8,16bit

像素排列

ColorType 像素排列
Greyscale Y
Truecolour RGB
Indexed-colour i
Greyscale with alpha RGBA
Truecolour with alpha YA

解析文檔頭 IHDR, 分配圖像時分, 每行像素多壹字節,記錄『Filter』濾波值,『0,1,2,3,4』伍種濾波;

if(IHDR->ColorType == 0)

png->bitCount = IHDR->BitDepth;

0:Greyscale:灰度圖像,1,2,4,8,16bit
if(IHDR->ColorType == 2)

png->bitCount = IHDR->BitDepth * 3;

2:Truecolour:真彩色圖像,8,16bit
if(IHDR->ColorType == 3)

png->bitCount = IHDR->BitDepth;

3:Indexed-colour:索引彩色圖像,1,2,4,8bit
if(IHDR->ColorType == 4)

png->bitCount = IHDR->BitDepth * 2;

4:Greyscale with alpha:帶α通道資料灰度圖像,8,16bit
if(IHDR->ColorType == 6)

png->bitCount = IHDR->BitDepth * 4;

6:Truecolour with alpha:帶α通道資料真彩色圖像,8,16bit
png->width  = PNG_VALUE32(IHDR->width); 像素寬
png->height = PNG_VALUE32(IHDR->height); 像素高
int byte_count  = (float)png->bitCount / 8.0f; 像素大小 BIT to BYTE
png->buffer_size = (png->width * png->height * byte_count) + (png->height * 1); 圖像数据長度,每行多壹字節
png->buffer = (PBYTE)malloc(png->buffer_size); 圖像数据

定義色盤RGB

typedef struct PNG_RGB_TYP { 色盤3字節
    BYTE red;
    BYTE green;
    BYTE blue;
} PNG_RGB, *PNG_RGB_PTR;  

定義色盤RGBA

typedef struct PNG_RGBA_TYP { 色盤4字節
    BYTE red;
    BYTE green;
    BYTE blue;
    BYTE alpha; 透明混合,透明0x00~實體0xFF
} PNG_RGBA, *PNG_RGBA_PTR;  

定義PNG色盤結构 ‘PLTE’

typedef struct PNG_PLTE_TYP {  
int length; Data長度
DWORD type; 標記’PLTE’
PNG_RGB palette[256]; 至多256色盤
DWORD CRC32; CRC32校驗
} PNG_PLTE, *PNG_PLTE_PTR;  

分析PNG色盤 ‘PLTE’

int length = PNG_VALUE32(chunk->length); 長度
int count = length / 3; 色盤量
for(int index = 0; index < count; ++index){  
png->palette[index].red      = PLTE->palette[index].red ;
png->palette[index].green  = PLTE->palette[index].green ;
png->palette[index].blue    = PLTE->palette[index].blue ;
png->palette[index].alpha  = 0xff;    } 透明0x00~實體0xFF

定義透明像素結构’tRNS’

typedef struct PNG_tRNS_TYP{  
int length; 數據長度
DWORD type; 標記’tRNS’
union {  
WORD grey; 顏色類型 0
struct {WORD Red,Green,Blue;}; 顏色類型  2
BYTE palette[256]; 顏色類型  3
DWORD CRC32; CRC32校驗
}PNG_tRNS,*PNG_tRNS_PTR;  

分析透明像素

PNG_tRNS_PTR tRNS; 透明像素
tRNS->length = PNG_VALUE32(tRNS->length); 數據長度
if(IHDR->ColorType == 0 )

tRNS->grey = PNG_VALUE16(tRNS->grey);

0:Greyscale:灰度圖像,1,2,4,8,16bit
if(IHDR->ColorType == 2  ) {

tRNS->Red   = PNG_VALUE16(tRNS->Red);

tRNS->Green = PNG_VALUE16(tRNS->Green);

tRNS->Blue  = PNG_VALUE16(tRNS->Blue);

}

2:Truecolour:真彩色圖像,8,16bit
if(IHDR->ColorType == 3) {

length = tRNS->length;

for(int i = 0; i < length; ++i) {

index = tRNS->palette[i];

png->palette[index].alpha = 0x00;

}

3:Indexed-colour:索引彩色圖像,1,2,4,8bit

透明0x00~實體0xFF

定義背景色結构 ‘bKGD’

typedef struct PNG_bKGD_TYP{  
int length; Data長度
DWORD type; 標記’bKGD’
union {  
WORD Greyscale; 顏色類型0 and 4灰度
struct{WORD Red,Green,Blue;}; 顏色類型2 and 6 -RGB三色
BYTE Palette_Index;}; 顏色類型3色盤索引
DWORD CRC32; CRC32校驗
}PNG_bKGD,*PNG_bKGD_PTR;  

分析背景色’bKGD’

int length = PNG_VALUE32(bKGD->length); 長度
if(IHDR->ColorType == 0 || IHDR->ColorType == 4)

bKGD->Greyscale = PNG_VALUE16(bKGD->Greyscale);

灰度圖像 – 帶α通道資料灰度圖像
if(IHDR->ColorType == 2 || IHDR->ColorType == 6) {

bKGD->Red = PNG_VALUE16(bKGD->Red);

bKGD->Green = PNG_VALUE16(bKGD->Green);

bKGD->Blue = PNG_VALUE16(bKGD->Blue);

}

真彩色圖像 – 帶α通道資料真彩色圖像
if(IHDR->ColorType == 3)

bKGD->Palette_Index = bKGD->Palette_Index;

索引彩色圖像

定義圖像數值結构 ‘IDAT’, 止數據塊可能有多個.

typedef struct PNG_IDAT_TYP {  
int length;  Data長度
DWORD type; 標記’PLTE’
BYTE data[1]; 經壓縮圖像數據
DWORD CRC32; CRC32校驗
} PNG_IDAT, *PNG_IDAT_PTR;  

每行像素多壹字節,記錄『Filter』濾波值,『0,1,2,3,4』伍種濾波;

int length = PNG_VALUE32(IDAT->length); 長度
int size = png->buffer_size – png->length; 剩余記憶體
Uncompress_Data_gZip(png->buffer + png->length, &size,

IDAT->data,length,

&png->stream);

解压
png->length =  png->length + size; 累積積數據長度

結束 IEND,檢測到’IEND’數據塊,己到文檔未端.

typedef struct PNG_IEND_TAG{  
int length; Data長度0
DWORD type; 標記’IEND’
DWORD CRC32; CRC32校驗
}PNG_IEND, *PNG_IEND_PTR;  

以顏色類型分別進行『反濾波』, 係每行像素首字節『濾波』值,有『0,1,2,3,4』伍款濾波, 以每粒像素單獨『復位』,帶α通道真彩色圖像8bit為例.

以cbax像素排列支缓伍款濾波

c b
a x

濾波算法

filter濾波 濾波 復位
0= None Filt(x) = Orig(x) Recon(x) = Filt(x)
1= Sub Filt(x) = Orig(x) – Orig(a) Recon(x) = Filt(x) + Recon(a)
2= Up Filt(x) = Orig(x) – Orig(b) Recon(x) = Filt(x) + Recon(b)
3= Average Filt(x) = Orig(x) – floor((Orig(a) + Orig(b)) / 2) Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
4= Paeth Filt(x) = Orig(x) – PaethPredictor(Orig(a), Orig(b), Orig(c)) Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))

復位算法

int byte_count   = 4; 每粒像素4字節
int pixel_size = png->width * png->height; 總像素量
int buffer_size = png->width * png->height * byte_count; 總字節量
PBYTE buffer = (PBYTE)malloc(buffer_size); 反濾波影像數據
int col_size_recon = (png->width * 4) ; 復位後每行字節量
int col_size_filt  = (png->width * 4)  + 1; 濾波後每行字節量
for(int j = 0; j < png->height ; ++j) 每行像素
int filter = png->buffer[col_size_filt * j]; 行首濾波=1 byte

處理filter濾波0= None

if(filter == 0) { None
for(int i = 0; i < png->width; ++i){ 逐像素復位
Red = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 0]; x像素紅色
Green = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 1]; x像素藍色
Blue = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 2]; x像素錄色
Alpha = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 3]; x像素透明
buffer[(col_size_recon * j) + (i * 4) + 0] = Red; 復位像素紅色
buffer[(col_size_recon * j) + (i * 4) + 1] = Green; 復位像素藍色
buffer[(col_size_recon * j) + (i * 4) + 2] = Blue; 復位像素綠色
buffer[(col_size_recon * j) + (i * 4) + 3] = Alpha;         }} 復位像素透明

處理filter濾波1 =Sub

if(filter == 1)          { Sub
for(int i = 0; i < png->width; ++i){ 逐像素復位
Red = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 0]; x像素紅色
Green = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 1]; x像素藍色
Blue = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 2]; x像素錄色
Alpha = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 3]; x像素透明
aRed = aGreen = aBlue = aAlpha = 0; 清零
if(i > 0) {  
aRed = buffer[(col_size_recon * j) + ((i-1) * 4) + 0]; a像素紅色
aGreen = buffer[(col_size_recon * j) + ((i-1) * 4) + 1]; a像素藍色
aBlue = buffer[(col_size_recon * j) + ((i-1) * 4) + 2]; a像素綠色
aAlpha = buffer[(col_size_recon * j) + ((i-1) * 4) + 3]; } a像素透明
buffer[(col_size_recon * j) + (i * 4) + 0] = Red + aRed; 復位像素紅色
buffer[(col_size_recon * j) + (i * 4) + 1] = Green + aGreen; 復位像素藍色
buffer[(col_size_recon * j) + (i * 4) + 2] = Blue + aBlue; 復位像素綠色
buffer[(col_size_recon * j) + (i * 4) + 3] = Alpha + aAlpha; }} 復位像素透明

處理filter濾波2 =Up

if(filter == 2){ Up
for( i = 0; i < png->width; ++i){ 逐像素復位
Red = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 0]; x像素紅色
Green = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 1]; x像素藍色
Blue = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 2]; x像素錄色
Alpha = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 3]; x像素透明
bRed = bGreen = bBlue = bAlpha = 0; 清零
if(j > 0) {  
bRed = buffer[(col_size_recon * (j-1)) + (i * 4) + 0]; b像素紅色
bGreen = buffer[(col_size_recon * (j-1)) + (i * 4) + 1]; b像素藍色
bBlue = buffer[(col_size_recon * (j-1)) + (i * 4) + 2]; b像素綠色
bAlpha = buffer[(col_size_recon * (j-1)) + (i * 4) + 3];} b像素透明
buffer[(col_size_recon * j) + (i * 4) + 0] = Red + bRed; 復位像素紅色
buffer[(col_size_recon * j) + (i * 4) + 1] = Green + bGreen; 復位像素藍色
buffer[(col_size_recon * j) + (i * 4) + 2] = Blue + bBlue; 復位像素綠色
buffer[(col_size_recon * j) + (i * 4) + 3] = Alpha + bAlpha; }} 復位像素透明

處理filter濾波3 = Average

if(filter == 3)          { Average
for( i = 0; i < png->width; ++i){ 逐像素復位
Red = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 0]; x像素紅色
Green = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 1]; x像素藍色
Blue = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 2]; x像素錄色
Alpha = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 3]; x像素透明
aRed = aGreen = aBlue = aAlpha = 0; 清零
bRed = bGreen = bBlue = bAlpha = 0; 清零
if(i > 0) {  
aRed = buffer[(col_size_recon * j) + ((i-1) * 4) + 0]; a像素紅色
aGreen = buffer[(col_size_recon * j) + ((i-1) * 4) + 1]; a像素藍色
aBlue = buffer[(col_size_recon * j) + ((i-1) * 4) + 2]; a像素綠色
aAlpha = buffer[(col_size_recon * j) + ((i-1) * 4) + 3]; } a像素透明
if(j > 0) {  
bRed = buffer[(col_size_recon * (j-1)) + (i * 4) + 0]; b像素紅色
bGreen = buffer[(col_size_recon * (j-1)) + (i * 4) + 1]; b像素藍色
bBlue = buffer[(col_size_recon * (j-1)) + (i * 4) + 2]; b像素綠色
bAlpha = buffer[(col_size_recon * (j-1)) + (i * 4) + 3];} b像素透明
buffer[(col_size_recon * j) + (i * 4) + 0] = Red + ((aRed+bRed)/2); 復位像素紅色
buffer[(col_size_recon * j) + (i * 4) + 1] = Green + ((aGreen+bGreen)/2); 復位像素藍色
buffer[(col_size_recon * j) + (i * 4) + 2] = Blue + ((aBlue+bBlue)/2); 復位像素綠色
buffer[(col_size_recon * j) + (i * 4) + 3] = Alpha + ((aAlpha+bAlpha)/2); }} 復位像素透明

Paeth預測函式

BYTE PaethPredictor_PNG(BYTE a,BYTE b,BYTE c){ 預測函式
int p;  
int pa,pb,pc;  
p = a + b – c;  
pa = abs(p – a);  
pb = abs(p – b);  
pc = abs(p – c);  
if (pa <= pb && pa <= pc )  return a;  
else if (pb <= pc ) return b;  
else return c;}  

處理filter濾波4 =Paeth預測

if(filter == 3)          { Average
for( i = 0; i < png->width; ++i){ 逐像素復位
Red = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 0]; x像素紅色
Green = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 1]; x像素藍色
Blue = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 2]; x像素錄色
Alpha = png->buffer[(col_size_filt * j) + 1 + (i * 4) + 3]; x像素透明
aRed = aGreen = aBlue = aAlpha = 0; 清零
bRed = bGreen = bBlue = bAlpha = 0; 清零
cRed = cGreen = cBlue = cAlpha = 0; 清零
if(i > 0) {  
aRed = buffer[(col_size_recon * j) + ((i-1) * 4) + 0]; a像素紅色
aGreen = buffer[(col_size_recon * j) + ((i-1) * 4) + 1]; a像素藍色
aBlue = buffer[(col_size_recon * j) + ((i-1) * 4) + 2]; a像素綠色
aAlpha = buffer[(col_size_recon * j) + ((i-1) * 4) + 3]; } a像素透明
if(j > 0) {  
bRed = buffer[(col_size_recon * (j-1)) + (i * 4) + 0]; b像素紅色
bGreen = buffer[(col_size_recon * (j-1)) + (i * 4) + 1]; b像素藍色
bBlue = buffer[(col_size_recon * (j-1)) + (i * 4) + 2]; b像素綠色
bAlpha = buffer[(col_size_recon * (j-1)) + (i * 4) + 3];} b像素透明
if(i > 0 && j > 0) {  
cRed = buffer[(col_size_recon * (j-1)) + ((i-1) * 4) + 0]; c像素紅色
cGreen = buffer[(col_size_recon * (j-1)) + ((i-1) * 4) + 1]; c像素藍色
cBlue = buffer[(col_size_recon * (j-1)) + ((i-1) * 4) + 2]; c像素綠色
cAlpha = buffer[(col_size_recon * (j-1)) + ((i-1) * 4) + 3];} c像素透明
buffer[(col_size_recon * j) + (i * 4) + 0] = Red + PaethPredictor_PNG(aRed,bRed,cRed); 復位像素紅色
buffer[(col_size_recon * j) + (i * 4) + 1] = Green + PaethPredictor_PNG(aGreen,bGreen,cGreen); 復位像素藍色
buffer[(col_size_recon * j) + (i * 4) + 2] = Blue + PaethPredictor_PNG(aBlue,bBlue,cBlue); 復位像素綠色
buffer[(col_size_recon * j) + (i * 4) + 3] = Alpha + PaethPredictor_PNG(aAlpha,bAlpha,cAlpha);}} 復位像素透明

释放圖像

free(png->buffer); 释放
png->buffer = buffer; 替换復位圖像
png->bitCount = 32; rgba
png->buffer_size = buffer_size;  

 

評論