Final

Dependencies:   DebounceIn NokiaLCD WiflyInterface mbed

Fork of Websocket_Wifly_HelloWorld by Samuel Mokrani

Files at this revision

API Documentation at this revision

Comitter:
Ifrah
Date:
Thu Dec 13 23:59:40 2012 +0000
Parent:
2:c69a06fe81c0
Child:
4:6ed9c8bb82c3
Commit message:
Getting Google Maps based on the GPS coordinates

Changed in this revision

DebounceIn.lib Show annotated file Show diff for this revision Revisions of this file
GPS.cpp Show annotated file Show diff for this revision Revisions of this file
GPS.h Show annotated file Show diff for this revision Revisions of this file
NokiaLCD.lib Show annotated file Show diff for this revision Revisions of this file
WebSocketClient.lib Show diff for this revision Revisions of this file
WiflyInterface.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
picojpeg/picojpeg.c Show annotated file Show diff for this revision Revisions of this file
picojpeg/picojpeg.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DebounceIn.lib	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/AjK/code/DebounceIn/#91a2e988ba9d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GPS.cpp	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,137 @@
+/* mbed EM-406 GPS Module Library
+ * Copyright (c) 2008-2010, sford
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "GPS.h"
+
+GPS::GPS(PinName tx, PinName rx) : _gps(tx, rx) {
+    _gps.baud(4800);
+    longitude = 0.0;
+    latitude = 0.0;
+}
+
+int GPS::sample() {
+    float time;
+    char ns, ew;
+    int lock;
+
+    //return 1; //testing by Jigar
+    while (1) {
+        getline();
+        //printf("\n\rentered the GPS.sample while loop \n\r %s\n\r", msg);
+        
+        
+        // Check if it is a GPGGA msg (matches both locked and non-locked msg)
+        // $GPGGA,000116.031,,,,,0,00,,,M,0.0,M,,0000*52
+        /*
+        eg3. $GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
+                        1       2     3  4       5 6  7  8   9  10 11 12 13  14   15
+            1    = UTC of Position
+            2    = Latitude
+            3    = N or S
+            4    = Longitude
+            5    = E or W
+            6    = GPS quality indicator (0=invalid; 1=GPS fix; 2=Diff. GPS fix)
+            7    = Number of satellites in use [not those in view]
+            8    = Horizontal dilution of position
+            9    = Antenna altitude above/below mean sea level (geoid)
+            10   = Meters  (Antenna height unit)
+            11   = Geoidal separation (Diff. between WGS-84 earth ellipsoid and
+                   mean sea level.  -=geoid is below WGS-84 ellipsoid)
+            12   = Meters  (Units of geoidal separation)
+            13   = Age in seconds since last update from diff. reference station
+            14   = Diff. reference station ID#
+            15   = Checksum
+
+        */
+       // if (sscanf(msg, "GPGGA,%f,%f,%c,%f,%c,%*d,%*d, %*f,%*f,%*f,%*f,%*f,%*f,%d ", &time, &latitude, &ns, &longitude, &ew, &lock) >= 1) {
+        if(sscanf(msg, "GPGGA,%f,%f,%c,%f,%c,%d", &time, &latitude, &ns, &longitude, &ew, &lock) >= 1) { 
+
+            if (!lock) {
+                longitude = 0.0;
+                latitude = 0.0;
+                return 0;
+            } else {
+                if (ns == 'S') {
+                    latitude  *= -1.0;
+                }
+                if (ew == 'W') {
+                    longitude *= -1.0;
+                }
+                float degrees = trunc(latitude / 100.0f);
+                float minutes = latitude - (degrees * 100.0f);
+                latitude = degrees + minutes / 60.0f;
+                 degrees = trunc(longitude / 100.0f ); //degrees = trunc(longitude / 100.0f * 0.01f);
+                minutes = longitude - (degrees * 100.0f);
+                longitude = degrees + minutes / 60.0f;
+                return 1;
+            }
+        }  
+    }
+}
+
+float GPS::trunc(float v) {
+    if (v < 0.0) {
+        v*= -1.0;
+        v = floor(v);
+        v*=-1.0;
+    } else {
+        v = floor(v);
+    }
+    return v;
+}
+
+void GPS::getline() {
+    
+    while (_gps.getc() != '$');   // wait for the start of a line
+    // printf("entered the getline loop\n\r");
+    for (int i=0; i<256; i++) {
+        msg[i] = _gps.getc();
+        if (msg[i] == '\r') {
+            msg[i] = 0;
+            return;
+        }
+    }
+    error("Overflowed message limit");
+}
+
+/*
+$GPRMC,000115.039,V,,,,,,,291006,,*2C
+$GPGGA,000116.031,,,,,0,00,,,M,0.0,M,,0000*52
+$GPGSA,A,1,,,,,,,,,,,,,,,*1E
+$GPGSV,3,1,12,20,00,000,,10,00,000,,31,00,000,,27,00,000,*7C
+$GPGSV,3,2,12,19,00,000,,07,00,000,,04,00,000,,24,00,000,*76
+$GPGSV,3,3,12,16,00,000,,28,00,000,,26,00,000,,29,00,000,*78
+$GPRMC,000116.031,V,,,,,,,291006,,*27
+$GPGGA,000117.035,,,,,0,00,,,M,0.0,M,,0000*57
+$GPGSA,A,1,,,,,,,,,,,,,,,*1E
+$GPRMC,000117.035,V,,,,,,,291006,,*22
+$GPGGA,000118.039,,,,,0,00,,,M,0.0,M,,0000*54
+$GPGSA,A,1,,,,,,,,,,,,,,,*1E
+$GPRMC,000118.039,V,,,,,,,291006,,*21
+$GPGGA,000119.035,,,,,0,00,,,M,0.0,M,,0000*59
+$GPGSA,A,1,,,,,,,,,,,,,,,*1E
+$GPRMC,000119.035,V,,,,,,,291006,,*2C
+$GPGGA,000120.037,,,,,0,00,,,M,0.0,M,,0000*51
+$GPGSA,A,1,,,,,,,,,,,,,,,*1E
+$GPRMC,000120.037,V,,,,,,,291006,,*24
+
+*/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GPS.h	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,57 @@
+/* mbed EM-406 GPS Module Library
+ * Copyright (c) 2008-2010, sford
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "mbed.h"
+
+#ifndef MBED_GPS_H
+#define MBED_GPS_H
+
+/**  A GPS interface for reading from a Globalsat EM-406 GPS Module */
+class GPS {
+public:
+
+    /** Create the GPS interface, connected to the specified serial port
+     */    
+    GPS(PinName tx, PinName rx);
+    
+    /** Sample the incoming GPS data, returning whether there is a lock
+     * 
+     * @return 1 if there was a lock when the sample was taken (and therefore .longitude and .latitude are valid), else 0
+     */
+    int sample();
+    
+    /** The longitude (call sample() to set) */
+    float longitude;
+
+    /** The latitude (call sample() to set) */
+    float latitude;
+    
+private:
+    float trunc(float v);
+    void getline();
+    
+    Serial _gps;
+    char msg[256];
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NokiaLCD.lib	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/donde/code/NokiaLCD/#d829f93abd27
--- a/WebSocketClient.lib	Fri Aug 24 14:06:31 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/samux/code/WebSocketClient/#466f90b7849a
--- a/WiflyInterface.lib	Fri Aug 24 14:06:31 2012 +0000
+++ b/WiflyInterface.lib	Thu Dec 13 23:59:40 2012 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/WiflyInterface/#fb4494783863
+http://mbed.org/users/mbed_official/code/WiflyInterface/#8e54830d0df7
--- a/main.cpp	Fri Aug 24 14:06:31 2012 +0000
+++ b/main.cpp	Thu Dec 13 23:59:40 2012 +0000
@@ -1,28 +1,224 @@
 #include "mbed.h"
 #include "WiflyInterface.h"
-#include "Websocket.h"
+#include "NokiaLCD.h"
+#include "picojpeg.h"
+#include "DebounceIn.h"
+#include "GPS.h"
+#include <vector>
 
+#define DEFAULTZOOM 15
 
 /* wifly interface:
-*     - p9 and p10 are for the serial communication
-*     - p19 is for the reset pin
-*     - p26 is for the connection status
-*     - "mbed" is the ssid of the network
-*     - "password" is the password
+*     - p13 and p14 are for the serial communication
+*     - p28 is for the reset pin
+*     - p27 is for the connection status
+*     - 5th field is the ssid of the network
+*     - 6th field is the password
 *     - WPA is the security
 */
-WiflyInterface wifly(p9, p10, p19, p26, "mbed", "password", WPA);
+
+WiflyInterface wifly(p13, p14, p28, p27, "GTother", "GeorgeP@1927", WPA);
+Serial pc(USBTX,USBRX);
+LocalFileSystem local("local");// Create the local filesystem under the name "local"
+GPS gps(p9, p10);
+DebounceIn ZoomIn(p18);
+DebounceIn Refresh(p19);
+DebounceIn ZoomOut(p20);
+FILE *jpegfile;
+int jpeg_filesize = 0;
+int jpeg_filepos = 0;
+NokiaLCD lcd(p5, p7, p8, p11, NokiaLCD::LCD6610);
+int zoom = DEFAULTZOOM;
+
+
+unsigned char pjpeg_need_bytes_callback(unsigned char* pBuf, unsigned char buf_size, unsigned char *pBytes_actually_read, void *pCallback_data)
+{
+    unsigned int n = min((unsigned int)(jpeg_filesize - jpeg_filepos), (unsigned int)buf_size);
+    if (n && (fread(pBuf, 1, n, jpegfile) != n))
+        return PJPG_STREAM_READ_ERROR;
+    *pBytes_actually_read = (unsigned char)(n);
+    jpeg_filepos += n;
+    return 0;
+}
+
+void ReadJPEGFromFile(const char *filename, NokiaLCD *lcd)
+{
+    pjpeg_image_info_t imageInfo;
+    jpegfile = fopen(filename,"rb");
+    if(!jpegfile) {
+        pc.printf("File not Found: %s\n\r",filename);
+    }
+    fseek(jpegfile, 0, SEEK_END);
+    jpeg_filesize = ftell(jpegfile);
+    jpeg_filepos = 0;
+    fseek(jpegfile, 0, SEEK_SET);
+    int status = pjpeg_decode_init(&imageInfo, pjpeg_need_bytes_callback, NULL);
+    //const unsigned int row_pitch = imageInfo.m_width * imageInfo.m_comps;
+    int mcu_x = 0;
+    int mcu_y = 0;
+
+    for ( ; ; ) {
+        status = pjpeg_decode_mcu();
+
+        if (status) {
+            if (status != PJPG_NO_MORE_BLOCKS) {
+                //pc.printf("pjpeg_decode_mcu() failed with status %u\n", status);
+                fclose(jpegfile);
+                return;
+            }
 
-int main() {
-    wifly.init(); //Use DHCP
-    while (!wifly.connect());
-    printf("IP Address is %s\n\r", wifly.getIPAddress());
+            break;
+        }
+
+        if (mcu_y >= imageInfo.m_MCUSPerCol) {
+            fclose(jpegfile);
+            return;
+        }
+
+        // Copy MCU's pixel blocks into the destination bitmap.
+
+        for (int y = 0; y < imageInfo.m_MCUHeight; y += 8) {
+            const int by_limit = min(8, imageInfo.m_height - (mcu_y * imageInfo.m_MCUHeight + y));
+            for (int x = 0; x < imageInfo.m_MCUWidth; x += 8) {
+
+                unsigned int src_ofs = (x * 8U) + (y * 16U);
+                const unsigned char *pSrcR = imageInfo.m_pMCUBufR + src_ofs;
+                const unsigned char *pSrcG = imageInfo.m_pMCUBufG + src_ofs;
+                const unsigned char *pSrcB = imageInfo.m_pMCUBufB + src_ofs;
+
+                const int bx_limit = min(8, imageInfo.m_width - (mcu_x * imageInfo.m_MCUWidth + x));
+
+                if (imageInfo.m_scanType == PJPG_GRAYSCALE) {
+                    for (int by = 0; by < by_limit; by++) {
+                        for (int bx = 0; bx < bx_limit; bx++) {
+                            unsigned int color = ((*pSrcR++) << 16);
+                            (*lcd).pixel(mcu_x*imageInfo.m_MCUWidth+x+bx,mcu_y*imageInfo.m_MCUHeight+y+by,color);
+                        }
+                        pSrcR += (8 - bx_limit);
+                    }
+                } else {
+                    for (int by = 0; by < by_limit; by++) {
+                        for (int bx = 0; bx < bx_limit; bx++) {
+                            unsigned int color = ((*pSrcR++) << 16) | ((*pSrcG++) << 8) | (*pSrcB++);
+                            (*lcd).pixel((130-imageInfo.m_width)/2+mcu_x*imageInfo.m_MCUWidth+x+bx,(130-imageInfo.m_height)/2+mcu_y*imageInfo.m_MCUHeight+y+by,color);
+                        }
+
+                        pSrcR += (8 - bx_limit);
+                        pSrcG += (8 - bx_limit);
+                        pSrcB += (8 - bx_limit);
+
+                    }
+                }
+            }
+        }
+
+        mcu_x++;
+        if (mcu_x == imageInfo.m_MCUSPerRow) {
+            mcu_x = 0;
+            mcu_y++;
+        }
+    }
 
-    Websocket ws("ws://sockets.mbed.org:443/ws/demo/wo");
-    while (!ws.connect());
+    fclose(jpegfile);
+}
+
+void wiflyConnect()
+{
+    wifly.disconnect();
+    wifly.init();
+    while (!wifly.connect()) {
+        pc.printf("waiting\n\r");
+    }// join the network
+}
+
+void getNewImage(float latitude, float longitude, int zoom)
+{
+    TCPSocketConnection sock;
+    sock.connect("maps.googleapis.com", 80);
+    int ret = 0;
+    int total_bytes = 0;
+    char http_cmd[300];
+    sprintf(http_cmd, "GET /maps/api/staticmap?center=%f%%2C%f&zoom=%d&size=130x130&sensor=true&format=jpg-baseline HTTP/1.0\n\n",latitude,longitude,zoom);
+    sock.send_all(http_cmd, sizeof(http_cmd)-1);
 
-    while (1) {
-            ws.send("WebSocket Hello World over Wifly");
-            wait(1.0);
+    sock.set_blocking(false,10000);
+    char buffer[300];
+    FILE* pFile;
+    pc.printf("%s\n\r",http_cmd);
+    pFile = fopen ("/local/image.jpg","w");
+    pc.printf("Opening File\n\r");
+    bool found_start = false;
+    bool found_end = false;
+    while (true) {
+        ret = sock.receive(buffer, sizeof(buffer)-1);
+        if (ret <= 0) {
+            pc.printf("Ret < 0. Break. \n\r");
+            //wiflyConnect();
+            break;
+        }
+        buffer[ret] = '\0';
+        total_bytes += ret;
+        pc.printf("Received %d chars from server, total %d: %s\n\r", ret,total_bytes, buffer);
+        if(found_start) {
+            for(int i=1; i<ret; i++) {
+                if(buffer[i-1]==0xFF && buffer[i]==0xD9) { //jpg file end at FFD9
+                    pc.printf("found end. \n\r");
+                    fwrite(buffer,1,i+1,pFile);
+                    found_end = true;
+                    break;
+                }
+            }
+            if(!found_end)
+                fwrite(buffer,1,ret,pFile);
+            else {
+                pc.printf("Found Start and End. Break. \n\r");
+                break;
+            }
+
+        } else {
+            for(int i=1; i<ret; i++) {
+                if(buffer[i-1]==0xFF && buffer[i]==0xD8) { //jpg file starts from FFD8
+                    pc.printf("found start.\n\r");
+                    fwrite(&buffer[i-1],1,ret-i+1,pFile);
+                    found_start = true;
+                    break;
+                }
+            }
+        }
     }
+
+    fclose (pFile);
+    pc.printf("Closing File\n\r");
+    sock.close();
+    lcd.cls();
+    ReadJPEGFromFile("/local/IMAGE.JPG", &lcd);
+}
+
+int main()
+{
+    pc.printf("Hello\n\r");
+    wifly.init(); //Use DHCP
+    while (!wifly.connect()) {
+        pc.printf("waiting\n\r");
+    }// join the network
+    pc.printf("IP Address is %s\n", wifly.getIPAddress());
+
+    float longitude = 0.0;
+    float latitude = 0.0;
+    while(1) {
+        if(gps.sample()) {
+           // pc.printf("I'm at %f, %f\n", gps.longitude, gps.latitude);
+            longitude = gps.longitude;
+            latitude = gps.latitude;
+        } else {
+            pc.printf("Oh Dear! No lock :(\n");
+        }
+        if(Refresh)
+            getNewImage(latitude,longitude,zoom);
+        else if(ZoomIn)
+            getNewImage(latitude,longitude,++zoom);
+        else if(ZoomOut)
+            getNewImage(latitude,longitude,--zoom);
+    }
+    wifly.disconnect();
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/picojpeg/picojpeg.c	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,1736 @@
+//------------------------------------------------------------------------------
+// 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/picojpeg/picojpeg.h	Thu Dec 13 23:59:40 2012 +0000
@@ -0,0 +1,102 @@
+//------------------------------------------------------------------------------
+// picojpeg v1.0 - Public domain, Rich Geldreich <richgel99@gmail.com>
+//------------------------------------------------------------------------------
+#ifndef PICOJPEG_H
+#define PICOJPEG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Error codes
+enum
+{
+   PJPG_NO_MORE_BLOCKS = 1,
+   PJPG_BAD_DHT_COUNTS,
+   PJPG_BAD_DHT_INDEX,
+   PJPG_BAD_DHT_MARKER,
+   PJPG_BAD_DQT_MARKER,
+   PJPG_BAD_DQT_TABLE,
+   PJPG_BAD_PRECISION,
+   PJPG_BAD_HEIGHT,
+   PJPG_BAD_WIDTH,
+   PJPG_TOO_MANY_COMPONENTS,
+   PJPG_BAD_SOF_LENGTH,
+   PJPG_BAD_VARIABLE_MARKER,
+   PJPG_BAD_DRI_LENGTH,
+   PJPG_BAD_SOS_LENGTH,
+   PJPG_BAD_SOS_COMP_ID,
+   PJPG_W_EXTRA_BYTES_BEFORE_MARKER,
+   PJPG_NO_ARITHMITIC_SUPPORT,
+   PJPG_UNEXPECTED_MARKER,
+   PJPG_NOT_JPEG,
+   PJPG_UNSUPPORTED_MARKER,
+   PJPG_BAD_DQT_LENGTH,
+   PJPG_TOO_MANY_BLOCKS,
+   PJPG_UNDEFINED_QUANT_TABLE,
+   PJPG_UNDEFINED_HUFF_TABLE,
+   PJPG_NOT_SINGLE_SCAN,
+   PJPG_UNSUPPORTED_COLORSPACE,
+   PJPG_UNSUPPORTED_SAMP_FACTORS,
+   PJPG_DECODE_ERROR,
+   PJPG_BAD_RESTART_MARKER,
+   PJPG_ASSERTION_ERROR,
+   PJPG_BAD_SOS_SPECTRAL,
+   PJPG_BAD_SOS_SUCCESSIVE,
+   PJPG_STREAM_READ_ERROR,
+   PJPG_NOTENOUGHMEM,
+   PJPG_UNSUPPORTED_COMP_IDENT,
+   PJPG_UNSUPPORTED_QUANT_TABLE,
+   PJPG_UNSUPPORTED_MODE,
+};  
+
+// Scan types - currently only GRAYSCALE, YH1V1, and YH2V2 are actually supported.
+typedef enum
+{
+   PJPG_GRAYSCALE,
+   PJPG_YH1V1,
+   PJPG_YH2V1,
+   PJPG_YH1V2,
+   PJPG_YH2V2
+} pjpeg_scan_type_t;
+
+typedef struct
+{
+   // Image resolution
+   int m_width;
+   int m_height;
+   // Number of components (1 or 3)
+   int m_comps;
+   // Total number of minimum coded units (MCU's) per row/col.
+   int m_MCUSPerRow;
+   int m_MCUSPerCol;
+   // Scan type
+   pjpeg_scan_type_t m_scanType;
+   // MCU width/height in pixels
+   int m_MCUWidth;
+   int m_MCUHeight;
+   // Pointers to internal MCU pixel component buffers. 
+   // These buffers Will be filled with pixels each time pjpegDecodeMCU() is called successfully.
+   // Each MCU consists of (m_MCUWidth/8)*(m_MCUHeight/8) blocks (currently either 1 for greyscale/no subsampling, or 4 for H2V2 sampling factors), where each block is a contiguous array of 64 (8x8) bytes.
+   // For greyscale images, only the values in m_pMCUBufR are valid.
+   unsigned char *m_pMCUBufR;
+   unsigned char *m_pMCUBufG;
+   unsigned char *m_pMCUBufB;
+} pjpeg_image_info_t;
+
+typedef unsigned char (*pjpeg_need_bytes_callback_t)(unsigned char* pBuf, unsigned char buf_size, unsigned char *pBytes_actually_read, void *pCallback_data);
+
+// Initializes the decompressor. Returns 0 on success, or one of the above error codes on failure.
+// pNeed_bytes_callback will be called to fill the decompressor's internal input buffer.
+// Not thread safe.
+unsigned char pjpeg_decode_init(pjpeg_image_info_t *pInfo, pjpeg_need_bytes_callback_t pNeed_bytes_callback, void *pCallback_data);
+
+// Decompresses the file's next MCU. Returns 0 on success, PJPG_NO_MORE_BLOCKS if no more blocks are available, or an error code.
+// Must be called a total of m_MCUSPerRow*m_MCUSPerCol times to completely decompress the image.
+unsigned char pjpeg_decode_mcu(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PICOJPEG_H