📄 tonyjpegencoder.cpp
字号:
// First we copy bits and huffval
memcpy( pTbl->bits, pBits, sizeof(pTbl->bits) );
memcpy( pTbl->huffval, pVal, sizeof(pTbl->huffval) );
/* Figure C.1: make table of Huffman code length for each symbol */
/* Note that this is in code-length order. */
p = 0;
for (l = 1; l <= 16; l++) {
for (i = 1; i <= (int) pBits[l]; i++)
huffsize[p++] = (char) l;
}
huffsize[p] = 0;
lastp = p;
/* Figure C.2: generate the codes themselves */
/* Note that this is in code-length order. */
code = 0;
si = huffsize[0];
p = 0;
while (huffsize[p]) {
while (((int) huffsize[p]) == si) {
huffcode[p++] = code;
code++;
}
code <<= 1;
si++;
}
/* Figure C.3: generate encoding tables */
/* These are code and size indexed by symbol value */
/* Set any codeless symbols to have code length 0;
* this allows EmitBits to detect any attempt to emit such symbols.
*/
memset( pTbl->size, 0, sizeof( pTbl->size ) );
for (p = 0; p < lastp; p++) {
pTbl->code[ pVal[p] ] = huffcode[p];
pTbl->size[ pVal[p] ] = huffsize[p];
}
}
///////////////////////////////////////////////////////////////////////////////
//write soi, app0, Y_dqt, CbCr_dqt, sof, 4 * dht, sos.
void CTonyJpegEncoder::WriteJpegHeader(void)
{
write_soi();
write_app0();
write_dqt(0);//Y
write_dqt(1);//cbcr
write_sof(M_SOF0);
write_dht(0, 0);//m_htblYDC
write_dht(0, 1);//m_htblYAC
write_dht(1, 0);//m_htblCbCrDC
write_dht(1, 1);//m_htblCbCrAC
write_sos();
}
///////////////////////////////////////////////////////////////////////////////
void CTonyJpegEncoder::write_soi()
{
emit_marker(M_SOI);
}
void CTonyJpegEncoder::write_app0()
{
/*
* Length of APP0 block (2 bytes)
* Block ID (4 bytes - ASCII "JFIF")
* Zero byte (1 byte to terminate the ID string)
* Version Major, Minor (2 bytes - 0x01, 0x01)
* Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm)
* Xdpu (2 bytes - dots per unit horizontal)
* Ydpu (2 bytes - dots per unit vertical)
* Thumbnail X size (1 byte)
* Thumbnail Y size (1 byte)
*/
emit_marker(M_APP0);
emit_2bytes(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */
emit_byte(0x4A); /* Identifier: ASCII "JFIF" */
emit_byte(0x46);
emit_byte(0x49);
emit_byte(0x46);
emit_byte(0);
/* We currently emit version code 1.01 since we use no 1.02 features.
* This may avoid complaints from some older decoders.
*/
emit_byte(1); /* Major version */
emit_byte(1); /* Minor version */
emit_byte(1); /* Pixel size information */
emit_2bytes(300);
emit_2bytes(300);
emit_byte(0); /* No thumbnail image */
emit_byte(0);
}
void CTonyJpegEncoder::write_dqt(int index)//0:Y;1:CbCr
{
unsigned char* dqt;
if( index == 0 )
dqt = &m_dqtY[0];//changed from std with quality
else
dqt = &m_dqtCbCr[0];
//only allow prec = 0;
emit_marker(M_DQT);
emit_2bytes(67);//length
emit_byte(index);
int i;
unsigned char qval;
for (i = 0; i < 64; i++)
{
qval = (unsigned char) (dqt[jpeg_natural_order[i]]);
emit_byte(qval);
}
}
//currently support M_SOF0 baseline implementation
void CTonyJpegEncoder::write_sof(int code)
{
emit_marker(code);
emit_2bytes(17); //length
emit_byte(8);//cinfo->data_precision);
emit_2bytes(m_nHeight);
emit_2bytes(m_nWidth);
emit_byte(3);//cinfo->num_components);
//for Y
emit_byte(1);//compptr->component_id);
emit_byte(34);//(compptr->h_samp_factor << 4) + compptr->v_samp_factor);
emit_byte(0);//quant_tbl_no
//for Cb
emit_byte(2);//compptr->component_id);
emit_byte(17);//(compptr->h_samp_factor << 4) + compptr->v_samp_factor);
emit_byte(1);//quant_tbl_no
//for Cr
emit_byte(3);//compptr->component_id);
emit_byte(17);//(compptr->h_samp_factor << 4) + compptr->v_samp_factor);
emit_byte(1);//quant_tbl_no
}
void CTonyJpegEncoder::write_dht(int IsCbCr, int IsAc)
{
HUFFMAN_TABLE *htbl;
int index;
if( IsCbCr )
{
if( IsAc )
{
htbl = &m_htblCbCrAC;
index = 17;
}
else
{
htbl = &m_htblCbCrDC;
index = 1;
}
}
else
{
if( IsAc )
{
htbl = &m_htblYAC;
index = 16;
}
else
{
htbl = &m_htblYDC;
index = 0;
}
}
emit_marker(M_DHT);
int i, length = 0;
for (i = 1; i <= 16; i++)
length += htbl->bits[i];
emit_2bytes(length + 2 + 1 + 16);
emit_byte(index);
for (i = 1; i <= 16; i++)
emit_byte(htbl->bits[i]);
for (i = 0; i < length; i++)//varible-length
emit_byte(htbl->huffval[i]);
}
void CTonyJpegEncoder::write_sos()
{
emit_marker(M_SOS);
int length = 2 * 3 + 2 + 1 + 3;
emit_2bytes(length);
emit_byte(3);//cinfo->comps_in_scan
//Y
emit_byte(1);//index
emit_byte(0);//dc and ac tbl use 0-th tbl
//Cb
emit_byte(2);//index
emit_byte(0x11);//dc and ac tbl use 1-th tbl
//Cr
emit_byte(3);//index
emit_byte(0x11);//dc and ac tbl use 1-th tbl
emit_byte(0);//Ss
emit_byte(0x3F);//Se
emit_byte(0);// Ah/Al
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CTonyJpegEncoder::CompressImage(), the main function in this class !!
// Don't ask me its purpose, parameter lists, and return value, d:-)
bool CTonyJpegEncoder::CompressImage(
unsigned char *pInBuf, //source data, bgr format, 3 bytes per pixel
unsigned char *pOutBuf,//destination buffer, in jpg format
int nWidthPix, //image width in pixels
int nHeight, //height
int& nOutputBytes //return number of bytes being written
)
{
// Error handling
if(( pInBuf == 0 )||( pOutBuf == 0 ))
return false;
m_nWidth = nWidthPix;
m_nHeight = nHeight;
m_pOutBuf = pOutBuf;
//write soi, app0, Y_dqt, CbCr_dqt, sof, 4 * dht, sos.
WriteJpegHeader();
// let pOutBuf rewind to the first byte of output buffer
int nHeadBytes = m_pOutBuf - pOutBuf;//here being equal to header size
// bmp row bytes
int nRowBytes = ( m_nWidth * 3 + 3 ) / 4 * 4;
// exception
if(( nWidthPix <= 0 )||( nRowBytes <= 0 )||( nHeight <= 0 ))
return false;
// declares
int xPixel, yPixel, xTile, yTile, cxTile, cyTile, cxBlock, cyBlock;
int x, y, nTrueRows, nTrueCols, nTileBytes;
unsigned char byTile[768], *pTileRow, *pLastPixel, *pHolePixel;
// horizontal and vertical count of tile, macroblocks,
// or MCU(Minimum Coded Unit), in 16*16 pixels
cxTile = (nWidthPix + 15)>> 4;
cyTile = (nHeight + 15) >> 4;
// horizontal and vertical count of block, in 8*8 pixels
cxBlock = cxTile << 1;
cyBlock = cyTile << 1;
// first set output bytes as zero
nTileBytes = 0;
// three dc values set to zero, needed for compressing one new image
m_dcY = m_dcCb = m_dcCr = 0;
// Initialize size (in bits) and value to be written out
m_nPutBits = 0;
m_nPutVal = 0;
// Run all the tiles, or macroblocks, or MCUs
// Vertically flip image data
for( yTile = 0; yTile < cyTile; yTile++ )
{
for( xTile = 0; xTile < cxTile; xTile++ )
{
// Get tile starting pixel position
xPixel = xTile << 4;
yPixel = yTile << 4;
// Get the true number of tile columns and rows
nTrueRows = 16;
nTrueCols = 16;
if( yPixel + nTrueRows > nHeight )
nTrueRows = nHeight - yPixel;
if( xPixel + nTrueCols > nWidthPix )
nTrueCols = nWidthPix - xPixel;
// Prepare pointer to one row of this tile
// Vert-flip here !!!
pTileRow = pInBuf + (m_nHeight - yPixel) * nRowBytes + xPixel * 3;
// Get tile data from pInBuf into byTile. If not full, padding
// byTile with the bottom row and rightest pixel
for( y = 0; y < 16; y ++ )
{
if( y < nTrueRows )
{
// Get data of one row
pTileRow -= nRowBytes;
memcpy( byTile + y * 16 * 3, pTileRow, nTrueCols * 3 );
// padding to full tile with the rightest pixel
if( nTrueCols < 16 )
{
pLastPixel = pTileRow + (nTrueCols - 1) * 3;
pHolePixel = byTile + y * 16 * 3 + nTrueCols * 3;
for( x = nTrueCols; x < 16; x ++ )
{
memcpy( pHolePixel, pLastPixel, 3 );
pHolePixel += 3;
}
}
}
else
{
// padding the hole rows with the bottom row
memcpy( byTile + y * 16 * 3,
byTile + (nTrueRows - 1) * 16 * 3,
16 * 3 );
}
}
// Compress this full tile with jpeg algorithm here !!!!!
// The compressed data length for this tile is return by nTileBytes
if( ! CompressOneTile( byTile ))
return false;
}
}
// Maybe there are some bits left, send them here
if( m_nPutBits > 0 )
{
EmitLeftBits( );
}
// write EOI; end of Image
emit_marker(M_EOI);
nOutputBytes = m_pOutBuf - pOutBuf;
return true;
}
////////////////////////////////////////////////////////////////////////////////
// function Purpose: compress one 16*16 pixels with jpeg
// destination is m_pOutBuf, in jpg format
bool CTonyJpegEncoder::CompressOneTile(
unsigned char * pBgr //source data, in BGR format
)
{
// Three color components, 256 + 64 + 64 elements
int pYCbCr[384];
// The DCT outputs are returned scaled up by a factor of 8;
// they therefore have a range of +-8K for 8-bit source data
int coef[64];
// Color conversion and subsampling
// pY data is in block order, e.g.
// block 0 is from pY[0] to pY[63], block 1 is from pY[64] to pY[127]
BGRToYCbCrEx( pBgr, pYCbCr );
// Do Y/Cb/Cr components, Y: 4 blocks; Cb: 1 block; Cr: 1 block
int i;
for( i=0; i<6; i++ )
{
ForwardDct( pYCbCr + i*64, coef );
Quantize( coef, i ); //coef is both in and out
HuffmanEncode( coef, i );//output into m_pOutBuf
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Color convertion from bgr to ycbcr for one tile, 16*16 pixels
// Actually being not used for efficiency !!!!! Please use BGRToYCbCrEx()
void CTonyJpegEncoder::BGRToYCbCr(
unsigned char * pBgr, //tile source data, in BGR format, 768 bytes
unsigned char * pY, //out, Illuminance, 256 bytes
unsigned char * pCb, //out, Cb, 256 bytes
unsigned char * pCr //out, Cr, 256 bytes
)
{
int i;
unsigned char r, g, b, *pByte = pBgr, *py = pY, *pcb = pCb, *pcr = pCr;
for( i=0; i<256; i++ )
{
b = *(pByte ++);
g = *(pByte ++);
r = *(pByte ++);
*(py++) = (unsigned char)((m_RToY[r] + m_GToY[g] + m_BToY[b] )>>16);
*(pcb++) = (unsigned char)((m_RToCb[r] + m_GToCb[g] + m_BToCb[b])>>16);
*(pcr++) = (unsigned char)((m_RToCr[r] + m_GToCr[g] + m_BToCr[b])>>16);
}
}
////////////////////////////////////////////////////////////////////////////////
// (1) Color convertion from bgr to ycbcr for one tile, 16*16 pixels;
// (2) Y has 4 blocks, with block 0 from pY[0] to pY[63],
// block 1 from pY[64] to pY[127], block 2 from pY[128] to pY[191], ...
// (3) With Cb/Cr subsampling, i.e. 2*2 pixels get one Cb and one Cr
// IJG use average for better performance; we just pick one from four
// (4) Do unsigned->signed conversion, i.e. substract 128
void CTonyJpegEncoder::BGRToYCbCrEx(
unsigned char * pBgr, //in, tile data, in BGR format, 768 bytes
int * pBlock //out, Y: 256; Cb: 64; Cr: 64
)
{
int x, y, *py[4], *pcb, *pcr;
unsigned char r, g, b, *pByte;
pByte = pBgr;
for( x = 0; x < 4; x++ )
py[ x ] = pBlock + 64 * x;
pcb = pBlock + 256;
pcr = pBlock + 320;
for( y=0; y<16; y++ )
{
for( x=0; x<16; x++ )
{
b = *(pByte ++);
g = *(pByte ++);
r = *(pByte ++);
// block number is ((y/8) * 2 + x/8): 0, 1, 2, 3
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -