File content as of revision 0:c546b51ecf0b:
//------------------------------------------------------------------------------
// picojpeg v1.0 - Public domain, Rich Geldreich <richgel99@gmail.com>
// Last modified Nov. 27, 2010
//------------------------------------------------------------------------------
#include "picojpeg.h"
//------------------------------------------------------------------------------
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef signed char int8;
typedef signed short int16;
//------------------------------------------------------------------------------
// Change as needed - the PJPG_MAX_WIDTH/PJPG_MAX_HEIGHT checks are only present
// to quickly detect bogus files.
#define PJPG_MAX_WIDTH 16384
#define PJPG_MAX_HEIGHT 16384
#define PJPG_MAXCOMPSINSCAN 3
//------------------------------------------------------------------------------
typedef enum
{
M_SOF0 = 0xC0,
M_SOF1 = 0xC1,
M_SOF2 = 0xC2,
M_SOF3 = 0xC3,
M_SOF5 = 0xC5,
M_SOF6 = 0xC6,
M_SOF7 = 0xC7,
M_JPG = 0xC8,
M_SOF9 = 0xC9,
M_SOF10 = 0xCA,
M_SOF11 = 0xCB,
M_SOF13 = 0xCD,
M_SOF14 = 0xCE,
M_SOF15 = 0xCF,
M_DHT = 0xC4,
M_DAC = 0xCC,
M_RST0 = 0xD0,
M_RST1 = 0xD1,
M_RST2 = 0xD2,
M_RST3 = 0xD3,
M_RST4 = 0xD4,
M_RST5 = 0xD5,
M_RST6 = 0xD6,
M_RST7 = 0xD7,
M_SOI = 0xD8,
M_EOI = 0xD9,
M_SOS = 0xDA,
M_DQT = 0xDB,
M_DNL = 0xDC,
M_DRI = 0xDD,
M_DHP = 0xDE,
M_EXP = 0xDF,
M_APP0 = 0xE0,
M_APP15 = 0xEF,
M_JPG0 = 0xF0,
M_JPG13 = 0xFD,
M_COM = 0xFE,
M_TEM = 0x01,
M_ERROR = 0x100
} JPEG_MARKER;
#define RST0 0xD0
//------------------------------------------------------------------------------
const int8 ZAG[] =
{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
};
//------------------------------------------------------------------------------
// 128 bytes
static int16 gCoeffBuf[8*8];
// 8*8*4 bytes * 3 = 768
static uint8 gMCUBufR[256];
static uint8 gMCUBufG[256];
static uint8 gMCUBufB[256];
// 256 bytes
static int16 gQuant0[8*8];
static int16 gQuant1[8*8];
// 6 bytes
static int16 gLastDC[3];
typedef struct HuffTableT
{
uint16 mMinCode[16];
uint16 mMaxCode[16];
uint8 mValPtr[16];
} HuffTable;
// DC - 192
static HuffTable gHuffTab0;
static uint8 gHuffVal0[16];
static HuffTable gHuffTab1;
static uint8 gHuffVal1[16];
// AC - 672
static HuffTable gHuffTab2;
static uint8 gHuffVal2[256];
static HuffTable gHuffTab3;
static uint8 gHuffVal3[256];
static uint8 gValidHuffTables;
static uint8 gValidQuantTables;
static uint8 gTemFlag;
#define MAX_IN_BUF_SIZE 256
static uint8 gInBuf[MAX_IN_BUF_SIZE];
static uint8 gInBufOfs;
static uint8 gInBufLeft;
static uint16 gBitBuf;
static uint8 gBitsLeft;
//------------------------------------------------------------------------------
static uint16 gImageXSize;
static uint16 gImageYSize;
static uint8 gCompsInFrame;
static uint8 gCompIdent[3];
static uint8 gCompHSamp[3];
static uint8 gCompVSamp[3];
static uint8 gCompQuant[3];
static uint16 gRestartInterval;
static uint16 gNextRestartNum;
static uint16 gRestartsLeft;
static uint8 gCompsInScan;
static uint8 gCompList[3];
static uint8 gCompDCTab[3]; // 0,1
static uint8 gCompACTab[3]; // 0,1
static pjpeg_scan_type_t gScanType;
static uint8 gMaxBlocksPerMCU;
static uint8 gMaxMCUXSize;
static uint8 gMaxMCUYSize;
static uint16 gMaxMCUSPerRow;
static uint16 gMaxMCUSPerCol;
static uint16 gNumMCUSRemaining;
static uint8 gMCUOrg[6];
static pjpeg_need_bytes_callback_t g_pNeedBytesCallback;
static void *g_pCallback_data;
//------------------------------------------------------------------------------
static uint8 fillInBuf(void)
{
unsigned char status;
// Reserve a few bytes at the beginning of the buffer for putting back ("stuffing") chars.
gInBufOfs = 4;
gInBufLeft = 0;
status = (*g_pNeedBytesCallback)(gInBuf + gInBufOfs, MAX_IN_BUF_SIZE - gInBufOfs, &gInBufLeft, g_pCallback_data);
if (status)
return status;
return 0;
}
//------------------------------------------------------------------------------
static uint8 getChar(void)
{
if (!gInBufLeft)
{
fillInBuf();
if (!gInBufLeft)
{
gTemFlag = ~gTemFlag;
return gTemFlag ? 0xFF : 0xD9;
}
}
gInBufLeft--;
return gInBuf[gInBufOfs++];
}
//------------------------------------------------------------------------------
static void stuffChar(uint8 i)
{
gInBufOfs--;
gInBuf[gInBufOfs] = i;
gInBufLeft++;
}
//------------------------------------------------------------------------------
static uint8 getOctet(uint8 FFCheck)
{
uint8 c = getChar();
if ((FFCheck) && (c == 0xFF))
{
uint8 n = getChar();
if (n)
{
stuffChar(n);
stuffChar(0xFF);
}
}
return c;
}
//------------------------------------------------------------------------------
static uint16 getBits(uint8 numBits, uint8 FFCheck)
{
uint8 origBits = numBits;
uint16 ret = gBitBuf;
if (numBits > 8)
{
numBits -= 8;
gBitBuf <<= gBitsLeft;
gBitBuf |= getOctet(FFCheck);
gBitBuf <<= (8 - gBitsLeft);
ret = (ret & 0xFF00) | (gBitBuf >> 8);
}
if (gBitsLeft < numBits)
{
gBitBuf <<= gBitsLeft;
gBitBuf |= getOctet(FFCheck);
gBitBuf <<= (numBits - gBitsLeft);
gBitsLeft = 8 - (numBits - gBitsLeft);
}
else
{
gBitsLeft = (uint8)(gBitsLeft - numBits);
gBitBuf <<= numBits;
}
return ret >> (16 - origBits);
}
//------------------------------------------------------------------------------
static uint16 getBits1(uint8 numBits)
{
return getBits(numBits, 0);
}
//------------------------------------------------------------------------------
static uint16 getBits2(uint8 numBits)
{
return getBits(numBits, 1);
}
//------------------------------------------------------------------------------
static uint8 getBit(void)
{
uint8 ret = 0;
if (gBitBuf & 0x8000)
ret = 1;
if (!gBitsLeft)
{
gBitBuf |= getOctet(1);
gBitsLeft += 8;
}
gBitsLeft--;
gBitBuf <<= 1;
return ret;
}
//------------------------------------------------------------------------------
static uint16 getExtendTest(uint8 i)
{
switch (i)
{
case 0: return 0;
case 1: return 0x0001;
case 2: return 0x0002;
case 3: return 0x0004;
case 4: return 0x0008;
case 5: return 0x0010;
case 6: return 0x0020;
case 7: return 0x0040;
case 8: return 0x0080;
case 9: return 0x0100;
case 10: return 0x0200;
case 11: return 0x0400;
case 12: return 0x0800;
case 13: return 0x1000;
case 14: return 0x2000;
case 15: return 0x4000;
default: return 0;
}
}
//------------------------------------------------------------------------------
static int16 getExtendOffset(uint8 i)
{
switch (i)
{
case 0: return 0;
case 1: return ((-1)<<1) + 1;
case 2: return ((-1)<<2) + 1;
case 3: return ((-1)<<3) + 1;
case 4: return ((-1)<<4) + 1;
case 5: return ((-1)<<5) + 1;
case 6: return ((-1)<<6) + 1;
case 7: return ((-1)<<7) + 1;
case 8: return ((-1)<<8) + 1;
case 9: return ((-1)<<9) + 1;
case 10: return ((-1)<<10) + 1;
case 11: return ((-1)<<11) + 1;
case 12: return ((-1)<<12) + 1;
case 13: return ((-1)<<13) + 1;
case 14: return ((-1)<<14) + 1;
case 15: return ((-1)<<15) + 1;
default: return 0;
}
};
//------------------------------------------------------------------------------
static int16 huffExtend(uint16 x, uint8 s)
{
return ((x < getExtendTest(s)) ? ((int16)x + getExtendOffset(s)) : (int16)x);
}
//------------------------------------------------------------------------------
static uint8 huffDecode(const HuffTable* pHuffTable, const uint8* pHuffVal)
{
uint8 i = 0;
uint8 j;
uint16 code = getBit();
for ( ; ; )
{
uint16 maxCode;
if (i == 16)
return 0;
maxCode = pHuffTable->mMaxCode[i];
if ((code <= maxCode) && (maxCode != 0xFFFF))
break;
i++;
code <<= 1;
code |= getBit();
}
j = pHuffTable->mValPtr[i];
j = (uint8)(j + (code - pHuffTable->mMinCode[i]));
return pHuffVal[j];
}
//------------------------------------------------------------------------------
static void huffCreate(const uint8* pBits, HuffTable* pHuffTable)
{
uint8 i = 0;
uint8 j = 0;
uint16 code = 0;
for ( ; ; )
{
uint8 num = pBits[i];
if (!num)
{
pHuffTable->mMinCode[i] = 0x0000;
pHuffTable->mMaxCode[i] = 0xFFFF;
pHuffTable->mValPtr[i] = 0;
}
else
{
pHuffTable->mMinCode[i] = code;
pHuffTable->mMaxCode[i] = code + num - 1;
pHuffTable->mValPtr[i] = j;
j = (uint8)(j + num);
code = (uint16)(code + num);
}
code <<= 1;
i++;
if (i > 15)
break;
}
}
//------------------------------------------------------------------------------
static HuffTable* getHuffTable(uint8 index)
{
// 0-1 = DC
// 2-3 = AC
switch (index)
{
case 0: return &gHuffTab0;
case 1: return &gHuffTab1;
case 2: return &gHuffTab2;
case 3: return &gHuffTab3;
default: return 0;
}
}
//------------------------------------------------------------------------------
static uint8* getHuffVal(uint8 index)
{
// 0-1 = DC
// 2-3 = AC
switch (index)
{
case 0: return gHuffVal0;
case 1: return gHuffVal1;
case 2: return gHuffVal2;
case 3: return gHuffVal3;
default: return 0;
}
}
//------------------------------------------------------------------------------
static uint16 getMaxHuffCodes(uint8 index)
{
return (index < 2) ? 12 : 255;
}
//------------------------------------------------------------------------------
static uint8 readDHTMarker(void)
{
uint8 bits[16];
uint16 left = getBits1(16);
if (left < 2)
return PJPG_BAD_DHT_MARKER;
left -= 2;
while (left)
{
uint8 i, tableIndex, index;
uint8* pHuffVal;
HuffTable* pHuffTable;
uint16 count, totalRead;
index = (uint8)getBits1(8);
if ( ((index & 0xF) > 1) || ((index & 0xF0) > 0x10) )
return PJPG_BAD_DHT_INDEX;
tableIndex = ((index >> 3) & 2) + (index & 1);
pHuffTable = getHuffTable(tableIndex);
pHuffVal = getHuffVal(tableIndex);
gValidHuffTables |= (1 << tableIndex);
count = 0;
for (i = 0; i <= 15; i++)
{
uint8 n = (uint8)getBits1(8);
bits[i] = n;
count = (uint16)(count + n);
}
if (count > getMaxHuffCodes(tableIndex))
return PJPG_BAD_DHT_COUNTS;
for (i = 0; i < count; i++)
pHuffVal[i] = (uint8)getBits1(8);
totalRead = 1 + 16 + count;
if (left < totalRead)
return PJPG_BAD_DHT_MARKER;
left = (uint16)(left - totalRead);
huffCreate(bits, pHuffTable);
}
return 0;
}
//------------------------------------------------------------------------------
static void createWinogradQuant(int16* pQuant);
static uint8 readDQTMarker(void)
{
uint16 left = getBits1(16);
if (left < 2)
return PJPG_BAD_DQT_MARKER;
left -= 2;
while (left)
{
uint8 i;
uint8 n = (uint8)getBits1(8);
uint8 prec = n >> 4;
uint16 totalRead;
n &= 0x0F;
if (n > 1)
return PJPG_BAD_DQT_TABLE;
gValidQuantTables |= (n ? 2 : 1);
// read quantization entries, in zag order
for (i = 0; i < 64; i++)
{
uint16 temp = getBits1(8);
if (prec)
temp = (temp << 8) + getBits1(8);
if (n)
gQuant1[i] = (int16)temp;
else
gQuant0[i] = (int16)temp;
}
createWinogradQuant(n ? gQuant1 : gQuant0);
totalRead = 64 + 1;
if (prec)
totalRead += 64;
if (left < totalRead)
return PJPG_BAD_DQT_LENGTH;
left = (uint16)(left - totalRead);
}
return 0;
}
//------------------------------------------------------------------------------
static uint8 readSOFMarker(void)
{
uint8 i;
uint16 left = getBits1(16);
if (getBits1(8) != 8)
return PJPG_BAD_PRECISION;
gImageYSize = getBits1(16);
if ((!gImageYSize) || (gImageYSize > PJPG_MAX_HEIGHT))
return PJPG_BAD_HEIGHT;
gImageXSize = getBits1(16);
if ((!gImageXSize) || (gImageXSize > PJPG_MAX_WIDTH))
return PJPG_BAD_WIDTH;
gCompsInFrame = (uint8)getBits1(8);
if (gCompsInFrame > 3)
return PJPG_TOO_MANY_COMPONENTS;
if (left != (gCompsInFrame + gCompsInFrame + gCompsInFrame + 8))
return PJPG_BAD_SOF_LENGTH;
for (i = 0; i < gCompsInFrame; i++)
{
gCompIdent[i] = (uint8)getBits1(8);
gCompHSamp[i] = (uint8)getBits1(4);
gCompVSamp[i] = (uint8)getBits1(4);
gCompQuant[i] = (uint8)getBits1(8);
if (gCompQuant[i] > 1)
return PJPG_UNSUPPORTED_QUANT_TABLE;
}
return 0;
}
//------------------------------------------------------------------------------
// Used to skip unrecognized markers.
static uint8 skipVariableMarker(void)
{
uint16 left = getBits1(16);
if (left < 2)
return PJPG_BAD_VARIABLE_MARKER;
left -= 2;
while (left)
{
getBits1(8);
left--;
}
return 0;
}
//------------------------------------------------------------------------------
// Read a define restart interval (DRI) marker.
static uint8 readDRIMarker(void)
{
if (getBits1(16) != 4)
return PJPG_BAD_DRI_LENGTH;
gRestartInterval = getBits1(16);
return 0;
}
//------------------------------------------------------------------------------
// Read a start of scan (SOS) marker.
static uint8 readSOSMarker(void)
{
uint8 i;
uint16 left = getBits1(16);
uint8 spectral_start, spectral_end, successive_high, successive_low;
gCompsInScan = (uint8)getBits1(8);
left -= 3;
if ( (left != (gCompsInScan + gCompsInScan + 3)) || (gCompsInScan < 1) || (gCompsInScan > PJPG_MAXCOMPSINSCAN) )
return PJPG_BAD_SOS_LENGTH;
for (i = 0; i < gCompsInScan; i++)
{
uint8 cc = (uint8)getBits1(8);
uint8 c = (uint8)getBits1(8);
uint8 ci;
left -= 2;
for (ci = 0; ci < gCompsInFrame; ci++)
if (cc == gCompIdent[ci])
break;
if (ci >= gCompsInFrame)
return PJPG_BAD_SOS_COMP_ID;
gCompList[i] = ci;
gCompDCTab[ci] = (c >> 4) & 15;
gCompACTab[ci] = (c & 15);
}
spectral_start = (uint8)getBits1(8);
spectral_end = (uint8)getBits1(8);
successive_high = (uint8)getBits1(4);
successive_low = (uint8)getBits1(4);
left -= 3;
while (left)
{
getBits1(8);
left--;
}
return 0;
}
//------------------------------------------------------------------------------
static uint8 nextMarker(void)
{
uint8 c;
uint8 bytes = 0;
do
{
do
{
bytes++;
c = (uint8)getBits1(8);
} while (c != 0xFF);
do
{
c = (uint8)getBits1(8);
} while (c == 0xFF);
} while (c == 0);
// If bytes > 0 here, there where extra bytes before the marker (not good).
return c;
}
//------------------------------------------------------------------------------
// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
// encountered.
static uint8 processMarkers(uint8* pMarker)
{
for ( ; ; )
{
uint8 c = nextMarker();
switch (c)
{
case M_SOF0:
case M_SOF1:
case M_SOF2:
case M_SOF3:
case M_SOF5:
case M_SOF6:
case M_SOF7:
// case M_JPG:
case M_SOF9:
case M_SOF10:
case M_SOF11:
case M_SOF13:
case M_SOF14:
case M_SOF15:
case M_SOI:
case M_EOI:
case M_SOS:
{
*pMarker = c;
return 0;
}
case M_DHT:
{
readDHTMarker();
break;
}
// Sorry, no arithmitic support at this time. Dumb patents!
case M_DAC:
{
return PJPG_NO_ARITHMITIC_SUPPORT;
}
case M_DQT:
{
readDQTMarker();
break;
}
case M_DRI:
{
readDRIMarker();
break;
}
//case M_APP0: /* no need to read the JFIF marker */
case M_JPG:
case M_RST0: /* no parameters */
case M_RST1:
case M_RST2:
case M_RST3:
case M_RST4:
case M_RST5:
case M_RST6:
case M_RST7:
case M_TEM:
{
return PJPG_UNEXPECTED_MARKER;
}
default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
{
skipVariableMarker();
break;
}
}
}
// return 0;
}
//------------------------------------------------------------------------------
// Finds the start of image (SOI) marker.
static uint8 locateSOIMarker(void)
{
uint16 bytesleft;
uint8 lastchar = (uint8)getBits1(8);
uint8 thischar = (uint8)getBits1(8);
/* ok if it's a normal JPEG file without a special header */
if ((lastchar == 0xFF) && (thischar == M_SOI))
return 0;
bytesleft = 4096; //512;
for ( ; ; )
{
if (--bytesleft == 0)
return PJPG_NOT_JPEG;
lastchar = thischar;
thischar = (uint8)getBits1(8);
if (lastchar == 0xFF)
{
if (thischar == M_SOI)
break;
else if (thischar == M_EOI) //getBits1 will keep returning M_EOI if we read past the end
return PJPG_NOT_JPEG;
}
}
/* Check the next character after marker: if it's not 0xFF, it can't
be the start of the next marker, so the file is bad */
thischar = (uint8)((gBitBuf >> 8) & 0xFF);
if (thischar != 0xFF)
return PJPG_NOT_JPEG;
return 0;
}
//------------------------------------------------------------------------------
// Find a start of frame (SOF) marker.
static uint8 locateSOFMarker(void)
{
uint8 c;
uint8 status = locateSOIMarker();
if (status)
return status;
status = processMarkers(&c);
if (status)
return status;
switch (c)
{
case M_SOF2:
{
return PJPG_UNSUPPORTED_MODE;
}
case M_SOF0: /* baseline DCT */
{
status = readSOFMarker();
if (status)
return status;
break;
}
case M_SOF9:
{
return PJPG_NO_ARITHMITIC_SUPPORT;
}
case M_SOF1: /* extended sequential DCT */
default:
{
return PJPG_UNSUPPORTED_MARKER;
}
}
return 0;
}
//------------------------------------------------------------------------------
// Find a start of scan (SOS) marker.
static uint8 locateSOSMarker(uint8* pFoundEOI)
{
uint8 c;
uint8 status;
*pFoundEOI = 0;
status = processMarkers(&c);
if (status)
return status;
if (c == M_EOI)
{
*pFoundEOI = 1;
return 0;
}
else if (c != M_SOS)
return PJPG_UNEXPECTED_MARKER;
return readSOSMarker();
}
//------------------------------------------------------------------------------
static uint8 init(void)
{
gImageXSize = 0;
gImageYSize = 0;
gCompsInFrame = 0;
gRestartInterval = 0;
gCompsInScan = 0;
gValidHuffTables = 0;
gValidQuantTables = 0;
gTemFlag = 0;
gInBufOfs = 0;
gInBufLeft = 0;
gBitBuf = 0;
gBitsLeft = 8;
getBits1(8);
getBits1(8);
return 0;
}
//------------------------------------------------------------------------------
// This method throws back into the stream any bytes that where read
// into the bit buffer during initial marker scanning.
static void fixInBuffer(void)
{
/* In case any 0xFF's where pulled into the buffer during marker scanning */
if (gBitsLeft > 0)
stuffChar((uint8)gBitBuf);
stuffChar((uint8)(gBitBuf >> 8));
gBitsLeft = 8;
getBits2(8);
getBits2(8);
}
//------------------------------------------------------------------------------
// Restart interval processing.
static uint8 processRestart(void)
{
// Let's scan a little bit to find the marker, but not _too_ far.
// 1536 is a "fudge factor" that determines how much to scan.
uint16 i;
uint8 c = 0;
for (i = 1536; i > 0; i--)
if (getChar() == 0xFF)
break;
if (i == 0)
return PJPG_BAD_RESTART_MARKER;
for ( ; i > 0; i--)
if ((c = getChar()) != 0xFF)
break;
if (i == 0)
return PJPG_BAD_RESTART_MARKER;
// Is it the expected marker? If not, something bad happened.
if (c != (gNextRestartNum + M_RST0))
return PJPG_BAD_RESTART_MARKER;
// Reset each component's DC prediction values.
gLastDC[0] = 0;
gLastDC[1] = 0;
gLastDC[2] = 0;
gRestartsLeft = gRestartInterval;
gNextRestartNum = (gNextRestartNum + 1) & 7;
// Get the bit buffer going again...
gBitsLeft = 8;
getBits2(8);
getBits2(8);
return 0;
}
//------------------------------------------------------------------------------
static uint8 findEOI(void)
{
uint8 c;
uint8 status;
// Prime the bit buffer
gBitsLeft = 8;
getBits1(8);
getBits1(8);
// The next marker _should_ be EOI
status = processMarkers(&c);
if (status)
return status;
//gTotalBytesRead -= in_buf_left;
if (c != M_EOI)
return PJPG_UNEXPECTED_MARKER;
return 0;
}
//------------------------------------------------------------------------------
static uint8 checkHuffTables(void)
{
uint8 i;
for (i = 0; i < gCompsInScan; i++)
{
uint8 compDCTab = gCompDCTab[gCompList[i]];
uint8 compACTab = gCompACTab[gCompList[i]] + 2;
if ( ((gValidHuffTables & (1 << compDCTab)) == 0) ||
((gValidHuffTables & (1 << compACTab)) == 0) )
return PJPG_UNDEFINED_HUFF_TABLE;
}
return 0;
}
//------------------------------------------------------------------------------
static uint8 checkQuantTables(void)
{
uint8 i;
for (i = 0; i < gCompsInScan; i++)
{
uint8 compQuantMask = gCompQuant[gCompList[i]] ? 2 : 1;
if ((gValidQuantTables & compQuantMask) == 0)
return PJPG_UNDEFINED_QUANT_TABLE;
}
return 0;
}
//------------------------------------------------------------------------------
static uint8 initScan(void)
{
uint8 foundEOI;
uint8 status = locateSOSMarker(&foundEOI);
if (status)
return status;
if (foundEOI)
return PJPG_UNEXPECTED_MARKER;
status = checkHuffTables();
if (status)
return status;
status = checkQuantTables();
if (status)
return status;
gLastDC[0] = 0;
gLastDC[1] = 0;
gLastDC[2] = 0;
if (gRestartInterval)
{
gRestartsLeft = gRestartInterval;
gNextRestartNum = 0;
}
fixInBuffer();
return 0;
}
//------------------------------------------------------------------------------
static uint8 initFrame(void)
{
if (gCompsInFrame == 1)
{
if ((gCompHSamp[0] != 1) || (gCompVSamp[0] != 1))
return PJPG_UNSUPPORTED_SAMP_FACTORS;
gScanType = PJPG_GRAYSCALE;
gMaxBlocksPerMCU = 1;
gMCUOrg[0] = 0;
gMaxMCUXSize = 8;
gMaxMCUYSize = 8;
}
else if (gCompsInFrame == 3)
{
if ( ((gCompHSamp[1] != 1) || (gCompVSamp[1] != 1)) ||
((gCompHSamp[2] != 1) || (gCompVSamp[2] != 1)) )
return PJPG_UNSUPPORTED_SAMP_FACTORS;
if ((gCompHSamp[0] == 1) && (gCompVSamp[0] == 1))
{
gScanType = PJPG_YH1V1;
gMaxBlocksPerMCU = 3;
gMCUOrg[0] = 0;
gMCUOrg[1] = 1;
gMCUOrg[2] = 2;
gMaxMCUXSize = 8;
gMaxMCUYSize = 8;
}
else if ((gCompHSamp[0] == 2) && (gCompVSamp[0] == 2))
{
gScanType = PJPG_YH2V2;
gMaxBlocksPerMCU = 6;
gMCUOrg[0] = 0;
gMCUOrg[1] = 0;
gMCUOrg[2] = 0;
gMCUOrg[3] = 0;
gMCUOrg[4] = 1;
gMCUOrg[5] = 2;
gMaxMCUXSize = 16;
gMaxMCUYSize = 16;
}
else
return PJPG_UNSUPPORTED_SAMP_FACTORS;
}
else
return PJPG_UNSUPPORTED_COLORSPACE;
gMaxMCUSPerRow = (gImageXSize + (gMaxMCUXSize - 1)) >> ((gMaxMCUXSize == 8) ? 3 : 4);
gMaxMCUSPerCol = (gImageYSize + (gMaxMCUYSize - 1)) >> ((gMaxMCUYSize == 8) ? 3 : 4);
gNumMCUSRemaining = gMaxMCUSPerRow * gMaxMCUSPerCol;
return 0;
}
/*----------------------------------------------------------------------------*/
#define DCT_SCALE_BITS 7
#define DCT_SCALE (1U << DCT_SCALE_BITS)
#define DESCALE(x) (((x) + (1U << (DCT_SCALE_BITS - 1))) >> DCT_SCALE_BITS)
#define WFIX(x) ((x) * DCT_SCALE + 0.5f)
#define WINOGRAD_QUANT_SCALE_BITS 10
const uint8 gWinogradQuant[] =
{
128, 178, 178, 167, 246, 167, 151, 232,
232, 151, 128, 209, 219, 209, 128, 101,
178, 197, 197, 178, 101, 69, 139, 167,
177, 167, 139, 69, 35, 96, 131, 151,
151, 131, 96, 35, 49, 91, 118, 128,
118, 91, 49, 46, 81, 101, 101, 81,
46, 42, 69, 79, 69, 42, 35, 54,
54, 35, 28, 37, 28, 19, 19, 10,
};
static void createWinogradQuant(int16* pQuant)
{
uint8 i;
for (i = 0; i < 64; i++)
{
long x = pQuant[i];
x *= gWinogradQuant[i];
pQuant[i] = (int16)((x + (1 << (WINOGRAD_QUANT_SCALE_BITS - DCT_SCALE_BITS - 1))) >> (WINOGRAD_QUANT_SCALE_BITS - DCT_SCALE_BITS));
}
}
// 1/cos(4*pi/16)
// 362, 256+106
#define b1 362
// 1/cos(6*pi/16)
// 669, 256+256+157
#define b2 669
// 1/cos(4*pi/16)
// 362, 256+106
#define b3 362
// 1/cos(2*pi/16)
// 277, 256+21
#define b4 277
// 1/(cos(2*pi/16) + cos(6*pi/16))
// 196, 196
#define b5 196
static int16 imul_b1_b3(int16 w)
{
long x = (w * 362L);
x += 128L;
return (int16)(x >> 8);
}
static int16 imul_b2(int16 w)
{
long x = (w * 669L);
x += 128L;
return (int16)(x >> 8);
}
static int16 imul_b4(int16 w)
{
long x = (w * 277L);
x += 128L;
return (int16)(x >> 8);
}
static int16 imul_b5(int16 w)
{
long x = (w * 196L);
x += 128L;
return (int16)(x >> 8);
}
static uint8 clamp(int16 s)
{
if ((uint16)s > 255U)
{
if (s < 0)
return 0;
else if (s > 255)
return 255;
}
return (uint8)s;
}
static void idctRows(void)
{
uint8 i;
int16* pSrc = gCoeffBuf;
for (i = 0; i < 8; i++)
{
int16 src4 = *(pSrc+5);
int16 src7 = *(pSrc+3);
int16 x4 = src4 - src7;
int16 x7 = src4 + src7;
int16 src5 = *(pSrc+1);
int16 src6 = *(pSrc+7);
int16 x5 = src5 + src6;
int16 x6 = src5 - src6;
int16 tmp1 = imul_b5(x4 - x6);
int16 stg26 = imul_b4(x6) - tmp1;
int16 x24 = tmp1 - imul_b2(x4);
int16 x15 = x5 - x7;
int16 x17 = x5 + x7;
int16 tmp2 = stg26 - x17;
int16 tmp3 = imul_b1_b3(x15) - tmp2;
int16 x44 = tmp3 + x24;
int16 src0 = *(pSrc+0);
int16 src1 = *(pSrc+4);
int16 x30 = src0 + src1;
int16 x31 = src0 - src1;
int16 src2 = *(pSrc+2);
int16 src3 = *(pSrc+6);
int16 x12 = src2 - src3;
int16 x13 = src2 + src3;
int16 x32 = imul_b1_b3(x12) - x13;
int16 x40 = x30 + x13;
int16 x43 = x30 - x13;
int16 x41 = x31 + x32;
int16 x42 = x31 - x32;
*(pSrc+0) = x40 + x17;
*(pSrc+1) = x41 + tmp2;
*(pSrc+2) = x42 + tmp3;
*(pSrc+3) = x43 - x44;
*(pSrc+4) = x43 + x44;
*(pSrc+5) = x42 - tmp3;
*(pSrc+6) = x41 - tmp2;
*(pSrc+7) = x40 - x17;
pSrc += 8;
}
}
static void idctCols(void)
{
uint8 i;
int16* pSrc = gCoeffBuf;
for (i = 0; i < 8; i++)
{
int16 src4 = *(pSrc+5*8);
int16 src7 = *(pSrc+3*8);
int16 x4 = src4 - src7;
int16 x7 = src4 + src7;
int16 src5 = *(pSrc+1*8);
int16 src6 = *(pSrc+7*8);
int16 x5 = src5 + src6;
int16 x6 = src5 - src6;
int16 tmp1 = imul_b5(x4 - x6);
int16 stg26 = imul_b4(x6) - tmp1;
int16 x24 = tmp1 - imul_b2(x4);
int16 x15 = x5 - x7;
int16 x17 = x5 + x7;
int16 tmp2 = stg26 - x17;
int16 tmp3 = imul_b1_b3(x15) - tmp2;
int16 x44 = tmp3 + x24;
int16 src0 = *(pSrc+0*8);
int16 src1 = *(pSrc+4*8);
int16 x30 = src0 + src1;
int16 x31 = src0 - src1;
int16 src2 = *(pSrc+2*8);
int16 src3 = *(pSrc+6*8);
int16 x12 = src2 - src3;
int16 x13 = src2 + src3;
int16 x32 = imul_b1_b3(x12) - x13;
int16 x40 = x30 + x13;
int16 x43 = x30 - x13;
int16 x41 = x31 + x32;
int16 x42 = x31 - x32;
*(pSrc+0*8) = clamp(DESCALE(x40 + x17) + 128);
*(pSrc+1*8) = clamp(DESCALE(x41 + tmp2) + 128);
*(pSrc+2*8) = clamp(DESCALE(x42 + tmp3) + 128);
*(pSrc+3*8) = clamp(DESCALE(x43 - x44) + 128);
*(pSrc+4*8) = clamp(DESCALE(x43 + x44) + 128);
*(pSrc+5*8) = clamp(DESCALE(x42 - tmp3) + 128);
*(pSrc+6*8) = clamp(DESCALE(x41 - tmp2) + 128);
*(pSrc+7*8) = clamp(DESCALE(x40 - x17) + 128);
pSrc++;
}
}
/*----------------------------------------------------------------------------*/
static uint8 addAndClamp(uint8 a, int16 b)
{
b = a + b;
if ((uint16)b > 255U)
{
if (b < 0)
return 0;
else if (b > 255)
return 255;
}
return (uint8)b;
}
/*----------------------------------------------------------------------------*/
static uint8 subAndClamp(uint8 a, int16 b)
{
b = a - b;
if ((uint16)b > 255U)
{
if (b < 0)
return 0;
else if (b > 255)
return 255;
}
return (uint8)b;
}
/*----------------------------------------------------------------------------*/
// 103/256
//R = Y + 1.402 (Cr-128)
// 88/256, 183/256
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
// 198/256
//B = Y + 1.772 (Cb-128)
/*----------------------------------------------------------------------------*/
static void upsampleCb(uint8 srcOfs, uint8 dstOfs)
{
// Cb - affects G and B
uint8 x, y;
int16* pSrc = gCoeffBuf + srcOfs;
uint8* pDstG = gMCUBufG + dstOfs;
uint8* pDstB = gMCUBufB + dstOfs;
for (y = 0; y < 4; y++)
{
for (x = 0; x < 4; x++)
{
uint8 cb = (uint8)*pSrc++;
int16 cbG, cbB;
cbG = ((cb * 88U) >> 8U) - 44U;
pDstG[0] = subAndClamp(pDstG[0], cbG);
pDstG[1] = subAndClamp(pDstG[1], cbG);
pDstG[8] = subAndClamp(pDstG[8], cbG);
pDstG[9] = subAndClamp(pDstG[9], cbG);
cbB = (cb + ((cb * 198U) >> 8U)) - 227U;
pDstB[0] = addAndClamp(pDstB[0], cbB);
pDstB[1] = addAndClamp(pDstB[1], cbB);
pDstB[8] = addAndClamp(pDstB[8], cbB);
pDstB[9] = addAndClamp(pDstB[9], cbB);
pDstG += 2;
pDstB += 2;
}
pSrc = pSrc - 4 + 8;
pDstG = pDstG - 8 + 16;
pDstB = pDstB - 8 + 16;
}
}
/*----------------------------------------------------------------------------*/
// 103/256
//R = Y + 1.402 (Cr-128)
// 88/256, 183/256
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
// 198/256
//B = Y + 1.772 (Cb-128)
/*----------------------------------------------------------------------------*/
static void upsampleCr(uint8 srcOfs, uint8 dstOfs)
{
// Cr - affects R and G
uint8 x, y;
int16* pSrc = gCoeffBuf + srcOfs;
uint8* pDstR = gMCUBufR + dstOfs;
uint8* pDstG = gMCUBufG + dstOfs;
for (y = 0; y < 4; y++)
{
for (x = 0; x < 4; x++)
{
uint8 cr = (uint8)*pSrc++;
int16 crR, crG;
crR = (cr + ((cr * 103U) >> 8U)) - 179;
pDstR[0] = addAndClamp(pDstR[0], crR);
pDstR[1] = addAndClamp(pDstR[1], crR);
pDstR[8] = addAndClamp(pDstR[8], crR);
pDstR[9] = addAndClamp(pDstR[9], crR);
crG = ((cr * 183U) >> 8U) - 91;
pDstG[0] = subAndClamp(pDstG[0], crG);
pDstG[1] = subAndClamp(pDstG[1], crG);
pDstG[8] = subAndClamp(pDstG[8], crG);
pDstG[9] = subAndClamp(pDstG[9], crG);
pDstR += 2;
pDstG += 2;
}
pSrc = pSrc - 4 + 8;
pDstR = pDstR - 8 + 16;
pDstG = pDstG - 8 + 16;
}
}
/*----------------------------------------------------------------------------*/
static void copyY(uint8 dstOfs)
{
uint8 i;
uint8* pRDst = gMCUBufR + dstOfs;
uint8* pGDst = gMCUBufG + dstOfs;
uint8* pBDst = gMCUBufB + dstOfs;
int16* pSrc = gCoeffBuf;
for (i = 64; i > 0; i--)
{
uint8 c = (uint8)*pSrc++;
*pRDst++ = c;
*pGDst++ = c;
*pBDst++ = c;
}
}
/*----------------------------------------------------------------------------*/
static void convertCb(uint8 dstOfs)
{
uint8 i;
uint8* pDstG = gMCUBufG + dstOfs;
uint8* pDstB = gMCUBufB + dstOfs;
int16* pSrc = gCoeffBuf;
for (i = 64; i > 0; i--)
{
uint8 cb = (uint8)*pSrc++;
int16 cbG, cbB;
cbG = ((cb * 88U) >> 8U) - 44U;
*pDstG++ = subAndClamp(pDstG[0], cbG);
cbB = (cb + ((cb * 198U) >> 8U)) - 227U;
*pDstB++ = addAndClamp(pDstB[0], cbB);
}
}
/*----------------------------------------------------------------------------*/
static void convertCr(uint8 dstOfs)
{
uint8 i;
uint8* pDstR = gMCUBufR + dstOfs;
uint8* pDstG = gMCUBufG + dstOfs;
int16* pSrc = gCoeffBuf;
for (i = 64; i > 0; i--)
{
uint8 cr = (uint8)*pSrc++;
int16 crR, crG;
crR = (cr + ((cr * 103U) >> 8U)) - 179;
*pDstR++ = addAndClamp(pDstR[0], crR);
crG = ((cr * 183U) >> 8U) - 91;
*pDstG++ = subAndClamp(pDstG[0], crG);
}
}
/*----------------------------------------------------------------------------*/
static void transformBlock(uint8 mcuBlock)
{
idctRows();
idctCols();
switch (gScanType)
{
case PJPG_GRAYSCALE:
{
copyY(0);
break;
}
case PJPG_YH1V1:
{
switch (mcuBlock)
{
case 0:
{
copyY(0);
break;
}
case 1:
{
convertCb(0);
break;
}
case 2:
{
convertCr(0);
break;
}
}
break;
}
case PJPG_YH2V2:
{
switch (mcuBlock)
{
case 0:
{
copyY(0);
break;
}
case 1:
{
copyY(64);
break;
}
case 2:
{
copyY(128);
break;
}
case 3:
{
copyY(192);
break;
}
case 4:
{
upsampleCb(0, 0);
upsampleCb(4, 64);
upsampleCb(4*8, 128);
upsampleCb(4+4*8, 192);
break;
}
case 5:
{
upsampleCr(0, 0);
upsampleCr(4, 64);
upsampleCr(4*8, 128);
upsampleCr(4+4*8, 192);
break;
}
}
}
}
}
//------------------------------------------------------------------------------
static uint8 decodeNextMCU(void)
{
uint8 status;
uint8 mcuBlock;
if (gRestartInterval)
{
if (gRestartsLeft == 0)
{
status = processRestart();
if (status)
return status;
}
gRestartsLeft--;
}
for (mcuBlock = 0; mcuBlock < gMaxBlocksPerMCU; mcuBlock++)
{
uint8 componentID = gMCUOrg[mcuBlock];
uint8 compQuant = gCompQuant[componentID];
uint8 compDCTab = gCompDCTab[componentID];
uint8 numExtraBits, compACTab, k;
const int16* pQ = compQuant ? gQuant1 : gQuant0;
uint16 r, dc;
uint8 s = huffDecode(compDCTab ? &gHuffTab1 : &gHuffTab0, compDCTab ? gHuffVal1 : gHuffVal0);
r = 0;
numExtraBits = s & 0xF;
if (numExtraBits)
r = getBits2(numExtraBits);
dc = huffExtend(r, s);
dc = dc + gLastDC[componentID];
gLastDC[componentID] = dc;
gCoeffBuf[0] = dc * pQ[0];
compACTab = gCompACTab[componentID];
for (k = 1; k < 64; k++)
{
uint16 extraBits;
s = huffDecode(compACTab ? &gHuffTab3 : &gHuffTab2, compACTab ? gHuffVal3 : gHuffVal2);
extraBits = 0;
numExtraBits = s & 0xF;
if (numExtraBits)
extraBits = getBits2(numExtraBits);
r = s >> 4;
s &= 15;
if (s)
{
int16 ac;
if (r)
{
if ((k + r) > 63)
return PJPG_DECODE_ERROR;
while (r)
{
gCoeffBuf[ZAG[k++]] = 0;
r--;
}
}
ac = huffExtend(extraBits, s);
gCoeffBuf[ZAG[k]] = ac * pQ[k];
}
else
{
if (r == 15)
{
if ((k + 16) > 64)
return PJPG_DECODE_ERROR;
for (r = 16; r > 0; r--)
gCoeffBuf[ZAG[k++]] = 0;
k--; // - 1 because the loop counter is k
}
else
break;
}
}
while (k < 64)
gCoeffBuf[ZAG[k++]] = 0;
transformBlock(mcuBlock);
}
return 0;
}
//------------------------------------------------------------------------------
unsigned char pjpeg_decode_mcu(void)
{
uint8 status;
if (!gNumMCUSRemaining)
return PJPG_NO_MORE_BLOCKS;
status = decodeNextMCU();
if (status)
return status;
gNumMCUSRemaining--;
return 0;
}
//------------------------------------------------------------------------------
unsigned char pjpeg_decode_init(pjpeg_image_info_t *pInfo, pjpeg_need_bytes_callback_t pNeed_bytes_callback, void *pCallback_data)
{
uint8 status;
g_pNeedBytesCallback = pNeed_bytes_callback;
g_pCallback_data = pCallback_data;
status = init();
if (status)
return status;
status = locateSOFMarker();
if (status)
return status;
status = initFrame();
if (status)
return status;
status = initScan();
if (status)
return status;
pInfo->m_width = gImageXSize;
pInfo->m_height = gImageYSize;
pInfo->m_comps = gCompsInFrame;
pInfo->m_scanType = gScanType;
pInfo->m_MCUSPerRow = gMaxMCUSPerRow;
pInfo->m_MCUSPerCol = gMaxMCUSPerCol;
pInfo->m_MCUWidth = gMaxMCUXSize;
pInfo->m_MCUHeight = gMaxMCUYSize;
pInfo->m_pMCUBufR = gMCUBufR;
pInfo->m_pMCUBufG = gMCUBufG;
pInfo->m_pMCUBufB = gMCUBufB;
return 0;
}