圖檔-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像素排列支缓伍款濾波
濾波算法
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;
你必須登入才能發表留言。