前回確認した処理フローに従ってハフマン変換をコーディングしよう。
ファンクションはルックアップテーブル(LUT)から変換コードを取り出して
32bitバッファに詰め込む部分と、
32bitバッファ内のデータの有効ビットが8ビットを超えた時に
上位から8ビットずつ取り出してデータ保存用のバイト配列に写す部分に分け、
2つのファンクションで構成することにした。
で、以下をSub Functionsセクションに記述。
変換コードを32bitバッファに詰め込むファンクションは…
/*****
Huffman Convert
*****/
void Huffmann
(long Zig[][3], unsigned char HD[],
long *HDL, unsigned long *Buf, long *BFL){
int Didx; // Data Index
int i, k;
int ZC = 0; // Zero counter
for (k = 0; k < 3; k++){ // Y,Cb,Cr
// DC Huff-Code
Didx = Zig[0][k] + 2047;
*Buf = *Buf | (((unsigned long)L_DCc[L_Db[Didx]]) << (16 - *BFL));
*BFL += L_DCb[L_Db[Didx]];
Compress(HD, HDL, Buf, BFL);
// DC Data-Code
*Buf = *Buf | (((unsigned long)L_Dc[Didx]) << (16 - *BFL));
*BFL += L_Db[Didx];
Compress(HD, HDL, Buf, BFL);
for (i = 1; i < 64; i++){
if (Zig[i][k] == 0) ZC++;
else {
while (ZC > 15){ // ZRL処理
*Buf = *Buf | (((unsigned long)L_ACc[15][0]) << (16 - *BFL));
*BFL += L_ACb[15][0];
Compress(HD, HDL, Buf, BFL);
ZC -= 16;
}
// AC Huff-Code
Didx = Zig[i][k] + 2047;
*Buf = *Buf | (((unsigned long)L_ACc[ZC][L_Db[Didx]]) << (16 - *BFL));
*BFL += L_ACb[ZC][L_Db[Didx]];
Compress(HD, HDL, Buf, BFL);
// AC Data-Code
*Buf = *Buf | (((unsigned long)L_Dc[Didx]) << (16 - *BFL));
*BFL += L_Db[Didx];
Compress(HD, HDL, Buf, BFL);
ZC = 0;
}
}
if (ZC > 0){ // EOB処理
*Buf = *Buf | (((unsigned long)L_ACc[0][0]) << (16 - *BFL));
*BFL += L_ACb[0][0];
Compress(HD, HDL, Buf, BFL);
ZC = 0;
}
}
}
このファンクション自体は8x8 pixの処理ブロック1個を処理対象としている。
従って入力データは量子化処理済みのデータが入っているZig[]配列だ。
で、対象ブロックをルートファンクションで逐次回して行くから
32bitバッファはルート側で用意し、*Bufでアクセスする。
32bitバッファ内の有効bit長も同様で*BFLでアクセス。
データ保存用のバイト配列HD[]も確保するのはルート側であり、
そのデータ数カウンタもルートで確保し*HDLでアクセスすることとした。
ZRL処理の部分はAC成分のハフマンコード化において
ゼロランレングスが16を超えている時にゼロ値15個分を表すZRLコードを
挿入する処理である。
EOB処理の部分はやはりAC成分のハフマンコード化において
その成分(YCbCrごと)のブロック終端までゼロ値が続くことを表すEOBコードを
挿入する処理を行っている。
データ保存用のバイト配列に写すファンクションは…
/*****
Data Compression
*****/
void Compress
(unsigned char HD[], long *HDL,
unsigned long *Buf, long *BFL){
while (*BFL > 7){
HD[*HDL] = (unsigned char)(*Buf >> 24);
*Buf = *Buf << 8;
*BFL -= 8;
if (HD[*HDL] == 0xFF){ // FFデータには00を付加する
*HDL += 1;
HD[*HDL] = 0;
}
*HDL += 1;
}
}
本来は32bitバッファ内の有効bit長が8bitを超えた時のみ
処理すれば良いファンクションなのだが、
呼び出し側のHuffmannファンクションでは32bitバッファに
データが追記させるたびに無条件に呼び出すこととし、
有効bit長が8bitを超えたかの判断は、このCompressファンクションで行う。
(呼び出し側のHuffmannファンクションには呼び出しタイミングが6箇所あり、
これら全てに条件判定文を書くのはちょっとメンドクサイと思っただけの理由)
なお、if(HD[*HDL] == 0xFF)の部分は、Jpegのキマリゴトのため、
変換後のバイトデータが「FF」となった場合は、
直後に「00」を挿入しなければならないことに対処したもの。
さて、後は全体の流れをコントロールし、ファイル保存を行う
ルート・ファンクションと使用ファンクションのプロトタイプ宣言を記述すれば
サブサンプリング4:4:4のJpeg保存dllは完成する。
- つづく -