Jpeg编码完整流程解析

来源:互联网 发布:java反射作用 编辑:程序博客网 时间:2024/05/21 17:39

本文结github yinjinchao给出的jpeg编码源码demo进行讲解,另外编码原理部分有任何问题,大家可以参照下面的博客,这里主要讲代码实现

github链接为github源码

原理介绍Jpeg编码原理

当我们已经获取到一幅图像的所有像素rgb值时,第一步要做的就是rgb转yuv了,当然以下的过程我们也都仅考虑分解过后单个8 * 8 block的实现

1,颜色空间转换(以RGB到YUV为例)

void JpegEncoder::_convertColorSpace(int xPos, int yPos, char* yData, char* cbData, char* crData){for (int y=0; y<8; y++){unsigned char* p = m_rgbBuffer + (y+yPos)*m_width*3 + xPos*3;for (int x=0; x<8; x++){unsigned char B = *p++;unsigned char G = *p++;unsigned char R = *p++;yData[y*8+x] = (char)(0.299f * R + 0.587f * G + 0.114f * B - 128);cbData[y*8+x] = (char)(-0.1687f * R - 0.3313f * G + 0.5f * B );crData[y*8+x] = (char)(0.5f * R - 0.4187f * G - 0.0813f * B);}}}

这里的逻辑应该是非常直观的,m_rgbBuffer也就是保存rgb数据的数组起始地址了,经过颜色空间转换后,也就得到了三个表,分别为Y, U(Cr), V(Cb)

当然,从这里我们也能看出这里采用的YUV 4:4:4格式

2,DCT

我们将图像通过DCT转到频域范围,从而能够去除图像中的高频分量,获得较高的压缩比

对照DCT的公式,我们看下代码实现

void JpegEncoder::_foword_FDC(const char* channel_data, short* fdc_data){const float PI = 3.1415926f;for(int v=0; v<8; v++){for(int u=0; u<8; u++){float alpha_u = (u==0) ? 1/sqrt(8.0f) : 0.5f;float alpha_v = (v==0) ? 1/sqrt(8.0f) : 0.5f;float temp = 0.f;for(int x=0; x<8; x++){for(int y=0; y<8; y++){float data = channel_data[y*8+x];data *= cos((2*x+1)*u*PI/16.0f);data *= cos((2*y+1)*v*PI/16.0f);temp += data;}}temp *= alpha_u*alpha_v/m_YTable[ZigZag[v*8+u]];fdc_data[ZigZag[v*8+u]] = (short) ((short)(temp + 16384.5) - 16384);}}}

这里也是按部就班的依照公式,计算出每个u, v 对应的 F(u, v),那么下面的m_YTable是干嘛的呢

首先是由两个量化表(DQT),一个是亮度的量化表,一个是色度的量化表

const unsigned char Luminance_Quantization_Table[64] = {16,  11,  10,  16,  24,  40,  51,  61,12,  12,  14,  19,  26,  58,  60,  55,14,  13,  16,  24,  40,  57,  69,  56,14,  17,  22,  29,  51,  87,  80,  62,18,  22,  37,  56,  68, 109, 103,  77,24,  35,  55,  64,  81, 104, 113,  92,49,  64,  78,  87, 103, 121, 120, 101,72,  92,  95,  98, 112, 100, 103,  99};//-------------------------------------------------------------------------------const unsigned char Chrominance_Quantization_Table[64] = {17,  18,  24,  47,  99,  99,  99,  99,18,  21,  26,  66,  99,  99,  99,  99,24,  26,  56,  99,  99,  99,  99,  99,47,  66,  99,  99,  99,  99,  99,  99,99,  99,  99,  99,  99,  99,  99,  99,99,  99,  99,  99,  99,  99,  99,  99,99,  99,  99,  99,  99,  99,  99,  99,99,  99,  99,  99,  99,  99,  99,  99};const char ZigZag[64] ={     0, 1, 5, 6,14,15,27,28,    2, 4, 7,13,16,26,29,42,    3, 8,12,17,25,30,41,43,    9,11,18,24,31,40,44,53,    10,19,23,32,39,45,52,54,    20,22,33,38,46,51,55,60,    21,34,37,47,50,56,59,61,    35,36,48,49,57,58,62,63 };  

这两个量化表是根据人眼的视觉心理阈(貌似是这么叫的。。。)做出来的,当然为了能够手动调节量化的程度,源码作者搞了这么个东西,作者对quality_scale的解释是   
该参数在1~199之间,数值越大,压缩比例越高,跟着代码看确实也是如此

void JpegEncoder::_initQualityTables(int quality_scale){if(quality_scale<=0) quality_scale=1;if(quality_scale>=100) quality_scale=99;for(int i=0; i<64; i++){int temp = ((int)(Luminance_Quantization_Table[i] * quality_scale + 50) / 100);if (temp<=0) temp = 1;if (temp>0xFF) temp = 0xFF;m_YTable[ZigZag[i]] = (unsigned char)temp;temp = ((int)(Chrominance_Quantization_Table[i] * quality_scale + 50) / 100);if (temp<=0) temp = 1;if (temp>0xFF) temp = 0xFF;m_CbCrTable[ZigZag[i]] = (unsigned char)temp;}}

另外这个新生成的量化表并采用了之字形编排方式,也就是m_YTable和m_CbCrTable不是用i作为下表定位数组元素而是用了m_YTable[ZigZag[i]],这是由于u,v较大时,量化表中对应的量化台阶高度较大,另外高频分量的DCT系数较小,更容易产生0,也就是说量化后矩阵的右下角更容易出现0,那么为了0更多地能够连续出现,采用之字形编码的优势就非常明显了

接着,我们回到dct函数的最后两行代码,这里貌似源码作者有些笔误,这里temp值得计算直接都采用了m_YTable作为量化矩阵。。。最后一行四舍五入的操作也确实比较迷

3,DPCM与RLE

由于每个8*8 block里面DC的数值都比较大,为了节省空间,现在普遍采用的是DPCM的方法,就是除了第一个block存原始数值外,之后的每个block中DC全部都存与前一个DC的差值,而对于AC数值而言,之前采用的之字形编排方式,使得大量的0连续出现,从而能够采用RLE提高压缩率

在代码中这一步并没有直接的体现,我们接着往下看

4,Huffman编码

//-------------------------------------------------------------------------------const char Standard_DC_Luminance_NRCodes[] = { 0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };const unsigned char Standard_DC_Luminance_Values[] = { 4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11 };//-------------------------------------------------------------------------------const char Standard_DC_Chrominance_NRCodes[] = { 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };const unsigned char Standard_DC_Chrominance_Values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };//-------------------------------------------------------------------------------const char Standard_AC_Luminance_NRCodes[] = { 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };const unsigned char Standard_AC_Luminance_Values[] = {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,0xf9, 0xfa};//-------------------------------------------------------------------------------const char Standard_AC_Chrominance_NRCodes[] = { 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };const unsigned char Standard_AC_Chrominance_Values[] ={0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,0xf9, 0xfa};

Huffman编码表按照DC/AC,亮度/色度可分为4张表,另外我们在jpeg编码中用到的也并非上课学到的传统的Huffman编码,而是一种范式Huffman编码,它具有如下特点及优势(百度百科):范式哈夫曼编码最早由Schwartz[1964]提出,它是哈夫曼编码的一个子集。其中心思想是:使用某些强制的约定,仅通过很少的数据便能重构出哈夫曼编码树的结构。其中一种很重要的约定是数字序列属性(numerical sequence property),它要求相同长度的码字是连续整数的二进制描述。例如,假设码字长度为4的最小值为0010,那么其它长度为4的码字必为0011, 0100, 0101, ...;另一个约定:为了尽可能的利用编码空间,长度为i第一个码字f(i)能从长度为i-1的最后一个码字得出, 即:f(i) = 2(f(i-1)+1)。假定长度为4的最后一个码字为1001,那么长度为5的第一个码字便为10100。最后一个约定:码字长度最小的第一个编码从0开始。通过上述约定,解码器能根据每个码字的长度恢复出整棵哈夫曼编码树的结构

再定义了一堆Huffman表后,我们看看Huffman表的初始化操作

 struct BitString{    int length;        int value;};BitString m_Y_DC_Huffman_Table[12];BitString m_Y_AC_Huffman_Table[256];BitString m_CbCr_DC_Huffman_Table[12];BitString m_CbCr_AC_Huffman_Table[256];void JpegEncoder::_initHuffmanTables(void){memset(&m_Y_DC_Huffman_Table, 0, sizeof(m_Y_DC_Huffman_Table));_computeHuffmanTable(Standard_DC_Luminance_NRCodes, Standard_DC_Luminance_Values, m_Y_DC_Huffman_Table);memset(&m_Y_AC_Huffman_Table, 0, sizeof(m_Y_AC_Huffman_Table));_computeHuffmanTable(Standard_AC_Luminance_NRCodes, Standard_AC_Luminance_Values, m_Y_AC_Huffman_Table);memset(&m_CbCr_DC_Huffman_Table, 0, sizeof(m_CbCr_DC_Huffman_Table));_computeHuffmanTable(Standard_DC_Chrominance_NRCodes, Standard_DC_Chrominance_Values, m_CbCr_DC_Huffman_Table);memset(&m_CbCr_AC_Huffman_Table, 0, sizeof(m_CbCr_AC_Huffman_Table));_computeHuffmanTable(Standard_AC_Chrominance_NRCodes, Standard_AC_Chrominance_Values, m_CbCr_AC_Huffman_Table);}void JpegEncoder::_computeHuffmanTable(const char* nr_codes, const unsigned char* std_table, BitString* huffman_table){    unsigned char pos_in_table = 0;    unsigned short code_value = 0;    for(int k = 1; k <= 16; k++)    {        for(int j = 1; j <= nr_codes[k-1]; j++)        {            huffman_table[std_table[pos_in_table]].value = code_value;            huffman_table[std_table[pos_in_table]].length = k;            pos_in_table++;            code_value++;        }        code_value <<= 1;    }  }

以Standard_Luminance_DC为例,我们看下Huffman编码表的生成过程

computeHuffmanTable中,共有两个循环,第一层循环对应nr_nodes每个元素的遍历,第二层则将nr_nodes中对应元素取出,并对Huffman表赋值,实际上,BitString这个数据结构也就对应了单个Huffman编码,length表示编码长度,value表示编码值。于是当k = 1和k = 2时,并没有对应的编码值; k = 3时,有7个编码值与其对应(3bit 编码总共有8个,这里遵照前缀码的规则,同时也为了获得总体最小字节长度,设置了7个 3 bit 编码),按照范式Huffman编码,相同编码长度的编码值以1递增,这里也就对应了为什么code_value每次加1了;当k = 3的7个编码全部结束后,按照范式Huffman编码规则,f(i) = 2 * (f(i - 1) + 1),对code_value进行左移操作;依次类推,也就获得了整个Huffman编码表啦,当然这里使用十进制表示的编码序列的

computeHuffman以后也就得到了以下两张表,其中huffman_table以value进行寻址,对应数组元素的length和value分别为表中的Length和Bits对应的十进制数,建议结合Huffman编码 来看

从图中可以看出,编码的bit信息从上到下是按照范式Huffman编码的要求,每次递增1,bit位数增加时,加1后右移一位的要求的

JpegEncoder::BitString JpegEncoder::_getBitCode(int value){    BitString ret;    int v = (value>0) ? value : -value;        int length = 0;    for(length=0; v; v>>=1) length++;    ret.value = value>0 ? value : ((1<<length)+value-1);    ret.length = length;    return ret;};void JpegEncoder::_doHuffmanEncoding(const short* DU, short& prevDC, const BitString* HTDC, const BitString* HTAC, BitString* outputBitString, int& bitStringCounts){BitString EOB = HTAC[0x00];BitString SIXTEEN_ZEROS = HTAC[0xF0];int index=0;// encode DCint dcDiff = (int)(DU[0] - prevDC);prevDC = DU[0];if (dcDiff == 0) outputBitString[index++] = HTDC[0];else{BitString bs = _getBitCode(dcDiff);outputBitString[index++] = HTDC[bs.length];outputBitString[index++] = bs;}// encode ACsint endPos=63; //end0pos = first element in reverse order != 0while((endPos > 0) && (DU[endPos] == 0)) endPos--;for(int i=1; i<=endPos; ){int startPos = i;while((DU[i] == 0) && (i <= endPos)) i++;int zeroCounts = i - startPos;if (zeroCounts >= 16){for (int j=1; j<=zeroCounts/16; j++)outputBitString[index++] = SIXTEEN_ZEROS;zeroCounts = zeroCounts%16;}BitString bs = _getBitCode(DU[i]);outputBitString[index++] = HTAC[(zeroCounts << 4) | bs.length];outputBitString[index++] = bs;i++;}if (endPos != 63)outputBitString[index++] = EOB;bitStringCounts = index;}

接着就是真正Huffman编码的实现啦。DU就是之前量化后的DQT的结果,prevDC开始为0,之后一直表示每个8*8 block的直流分量值,HTDC和HTAC分别对应Huffman DC表和Huffman AC表,也就是上面放的两张图,我们继续看下具体的逻辑

_getBitCode的输入时一个有符号型整数value,输出则是该value对应的编码(编码长度与编码值)。正数的逻辑很简单了,长度就是该数值的有效数据位个数,编码值就是其本身;而对于负数,则需要以有效数据位长度(不包含符号位)为满量程,减去实际负数值为编码值。例如,-7,-3,-1的编码值都为0,而-6,-2的编码值为1。这也就刚好对应了jpeg标准码表

首先是DC部分的编码,从原博客上来看,主要分为以下几个步骤

1,获取DPCM值,即将本block中DC值减去上一个block的DC值,得到dcDiff

2,RLE编码,由于DC值位于数组的第一个,因此前面0的长度一直为0, 所以结果为(0,dcDiff)

3,Bit编码,通过查上表,得到dcDiff的bit值和bit长度,得到如下形式(0,BitCodingLength(dcDiff),BitCodingValue(dcDiff))

4,合并Bit编码中RLE编码和Bit编码长度两个数据,由于RLE编码对于DC而言一直为0, 那么就得到(BitCodingLength(dcDiff),BitCodingValue(dcDiff))

5,Huffman编码,得到(HuffmanCoding(BitCodingLength(dcDiff)),BitCodingValue(dcDiff))

接下来是AC编码

先找到DQT编码表中反向第一个不为零的index,即endPos

第一个循环找到AC中RLE编码0的个数,注意为了能用4个bit表示RLE值,RLE值不能超过15,所以当zerosCount >= 16时,需要按序存储一个或者多个(15,0),同样(15,0)在经过如上所示编码以后,应该是(15,0)-->(15,0,0)-->(15,0)-->(Huffman(0x0F),0),源程序貌似有些问题

接着,对找到的非零AC值,i,这里也经过上面DC编码的顺序,只不过这里RLE不是0啦,而是zerosCount,并且在组合RLE和BitCodingLength时,需要把zerosCount左移4位得到一个8bit数据的高4位。按此逻辑,一个block的Huffman编码部分也就完成啦

5,最后我们看下如何输出成一个jpeg文件

//-------------------------------------------------------------------------------void JpegEncoder::_write_byte_(unsigned char value, FILE* fp){    _write_(&value, 1, fp);}//-------------------------------------------------------------------------------void JpegEncoder::_write_word_(unsigned short value, FILE* fp){    // little endian in memory, lower byte in 'value' lies in lower address, so first byte of &value     // is the lower byte     unsigned short _value = ((value>>8)&0xFF) | ((value&0xFF)<<8);    _write_(&_value, 2, fp);}//-------------------------------------------------------------------------------void JpegEncoder::_write_(const void* p, int byteSize, FILE* fp){    fwrite(p, 1, byteSize, fp);}//-------------------------------------------------------------------------------void JpegEncoder::_write_jpeg_header(FILE* fp){    //SOI    _write_word_(0xFFD8, fp);        // marker = 0xFFD8    //APPO    _write_word_(0xFFE0,fp);        // marker = 0xFFE0    _write_word_(16, fp);            // length = 16 for usual JPEG, no thumbnail    _write_("JFIF", 5, fp);            // 'JFIF\0'    _write_byte_(1, fp);            // version_hi    _write_byte_(1, fp);            // version_low    _write_byte_(0, fp);            // xyunits = 0 no units, normal density    _write_word_(1, fp);            // xdensity    _write_word_(1, fp);            // ydensity    _write_byte_(0, fp);            // thumbWidth    _write_byte_(0, fp);            // thumbHeight    //DQT    _write_word_(0xFFDB, fp);        //marker = 0xFFDB    _write_word_(132, fp);            //size=132    _write_byte_(0, fp);            //QTYinfo== 0:  bit 0..3: number of QT = 0 (table for Y)                                     //                bit 4..7: precision of QT                                    //                bit 8    : 0    _write_(m_YTable, 64, fp);        //YTable    _write_byte_(1, fp);            //QTCbinfo = 1 (quantization table for Cb,Cr)    _write_(m_CbCrTable, 64, fp);    //CbCrTable    //SOFO    _write_word_(0xFFC0, fp);            //marker = 0xFFC0    _write_word_(17, fp);                //length = 17 for a truecolor YCbCr JPG    _write_byte_(8, fp);                //precision = 8: 8 bits/sample     _write_word_(m_height&0xFFFF, fp);    //height    _write_word_(m_width&0xFFFF, fp);    //width    _write_byte_(3, fp);                //nrofcomponents = 3: We encode a truecolor JPG    _write_byte_(1, fp);                //IdY = 1    _write_byte_(0x11, fp);                //HVY sampling factors for Y (bit 0-3 vert., 4-7 hor.)(SubSamp 1x1)    _write_byte_(0, fp);                //QTY  Quantization Table number for Y = 0    _write_byte_(2, fp);                //IdCb = 2    _write_byte_(0x11, fp);                //HVCb = 0x11(SubSamp 1x1)    _write_byte_(1, fp);                //QTCb = 1    _write_byte_(3, fp);                //IdCr = 3    _write_byte_(0x11, fp);                //HVCr = 0x11 (SubSamp 1x1)    _write_byte_(1, fp);                //QTCr Normally equal to QTCb = 1        //DHT    _write_word_(0xFFC4, fp);        //marker = 0xFFC4    _write_word_(0x01A2, fp);        //length = 0x01A2    _write_byte_(0, fp);            //HTYDCinfo bit 0..3    : number of HT (0..3), for Y =0                                    //            bit 4        : type of HT, 0 = DC table,1 = AC table                                    //            bit 5..7    : not used, must be 0    _write_(Standard_DC_Luminance_NRCodes, sizeof(Standard_DC_Luminance_NRCodes), fp);    //DC_L_NRC    _write_(Standard_DC_Luminance_Values, sizeof(Standard_DC_Luminance_Values), fp);        //DC_L_VALUE    _write_byte_(0x10, fp);            //HTYACinfo    _write_(Standard_AC_Luminance_NRCodes, sizeof(Standard_AC_Luminance_NRCodes), fp);    _write_(Standard_AC_Luminance_Values, sizeof(Standard_AC_Luminance_Values), fp); //we'll use the standard Huffman tables    _write_byte_(0x01, fp);            //HTCbDCinfo    _write_(Standard_DC_Chrominance_NRCodes, sizeof(Standard_DC_Chrominance_NRCodes), fp);    _write_(Standard_DC_Chrominance_Values, sizeof(Standard_DC_Chrominance_Values), fp);    _write_byte_(0x11, fp);            //HTCbACinfo    _write_(Standard_AC_Chrominance_NRCodes, sizeof(Standard_AC_Chrominance_NRCodes), fp);    _write_(Standard_AC_Chrominance_Values, sizeof(Standard_AC_Chrominance_Values), fp);    //SOS    _write_word_(0xFFDA, fp);        //marker = 0xFFC4    _write_word_(12, fp);            //length = 12    _write_byte_(3, fp);            //nrofcomponents, Should be 3: truecolor JPG    _write_byte_(1, fp);            //Idy=1    _write_byte_(0, fp);            //HTY    bits 0..3: AC table (0..3)                                    //        bits 4..7: DC table (0..3)    _write_byte_(2, fp);            //IdCb    _write_byte_(0x11, fp);            //HTCb    _write_byte_(3, fp);            //IdCr    _write_byte_(0x11, fp);            //HTCr    _write_byte_(0, fp);            //Ss not interesting, they should be 0,63,0    _write_byte_(0x3F, fp);            //Se    _write_byte_(0, fp);            //Bf}//-------------------------------------------------------------------------------void JpegEncoder::_write_bitstring_(const BitString* bs, int counts, int& newByte, int& newBytePos, FILE* fp){    unsigned short mask[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};        for(int i=0; i<counts; i++)    {        int value = bs[i].value;        int posval = bs[i].length - 1;        while (posval >= 0)        {            if ((value & mask[posval]) != 0)            {                newByte = newByte  | mask[newBytePos];            }            posval--;            newBytePos--;            if (newBytePos < 0)            {                // Write to stream                _write_byte_((unsigned char)(newByte), fp);                if (newByte == 0xFF)                {                    // Handle special case                    _write_byte_((unsigned char)(0x00), fp);                }                // Reinitialize                newBytePos = 7;                newByte = 0;            }        }    }}

write jpeg header这里就不介绍了,大家对着jpeg header标准看一看就好,这里主要看下如果由上一部的outputBitString输出成正式jpeg文件

write_bit_string其实也就对应着原博客的一个序列化过程

mask是一个最高位为1,其余低位为0的数组,用于比较得到一个数据中每个bit的值。。。原作者的做法是这样的,虽然我觉得移位大法会好一些

如上,一个BitString中有一个length和一个value数据,那么我怎么能把一串BitString按二进制序列化出来呢

这里的做法就是用了一个newByte和一个newBytePos,newByte用于记录每8个bit的值,而newBytePos用于按序标记每个bit,当newByte8个bit都被填充以后,newBytePos小于0,调用write_byte输出该Byte数据,那么如果是0xFF,为了不跟jpeg的标识冲突,就输出一个0x00,循环往复如此,就得到完整的bit流啦

0 0
原创粉丝点击