diff dwt/internal/image/JPEGFileFormat.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children e831403a80a9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGFileFormat.d	Sat Aug 09 17:00:02 2008 +0200
@@ -0,0 +1,1887 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved.  This source file is made available under the terms contained in the README file
+ * accompanying this program.  The README file should be located in the about_files directory of the
+ * plug-in that contains this source file.
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image;
+
+
+import java.io.IOException;
+
+import dwt.DWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.ImageLoaderEvent;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+
+public final class JPEGFileFormat : FileFormat {
+    int restartInterval;
+    JPEGFrameHeader frameHeader;
+    int imageWidth, imageHeight;
+    int interleavedMcuCols, interleavedMcuRows;
+    int maxV, maxH;
+    bool progressive;
+    int samplePrecision;
+    int nComponents;
+    int[][] frameComponents;
+    int[] componentIds;
+    byte[][] imageComponents;
+    int[] dataUnit;
+    int[][][] dataUnits;
+    int[] precedingDCs;
+    JPEGScanHeader scanHeader;
+    byte[] dataBuffer;
+    int currentBitCount;
+    int bufferCurrentPosition;
+    int restartsToGo;
+    int nextRestartNumber;
+    JPEGHuffmanTable[] acHuffmanTables;
+    JPEGHuffmanTable[] dcHuffmanTables;
+    int[][] quantizationTables;
+    int currentByte;
+    int encoderQFactor = 75;
+    int eobrun = 0;
+    /* JPEGConstants */
+    public static final int DCTSIZE = 8;
+    public static final int DCTSIZESQR = 64;
+    /* JPEGFixedPointConstants */
+    public static final int FIX_0_899976223 = 7373;
+    public static final int FIX_1_961570560 = 16069;
+    public static final int FIX_2_053119869 = 16819;
+    public static final int FIX_0_298631336 = 2446;
+    public static final int FIX_1_847759065 = 15137;
+    public static final int FIX_1_175875602 = 9633;
+    public static final int FIX_3_072711026 = 25172;
+    public static final int FIX_0_765366865 = 6270;
+    public static final int FIX_2_562915447 = 20995;
+    public static final int FIX_0_541196100 = 4433;
+    public static final int FIX_0_390180644 = 3196;
+    public static final int FIX_1_501321110 = 12299;
+    /* JPEGMarkerCodes */
+    public static final int APP0  = 0xFFE0;
+    public static final int APP15 = 0xFFEF;
+    public static final int COM   = 0xFFFE;
+    public static final int DAC   = 0xFFCC;
+    public static final int DHP   = 0xFFDE;
+    public static final int DHT   = 0xFFC4;
+    public static final int DNL   = 0xFFDC;
+    public static final int DRI   = 0xFFDD;
+    public static final int DQT   = 0xFFDB;
+    public static final int EOI   = 0xFFD9;
+    public static final int EXP   = 0xFFDF;
+    public static final int JPG   = 0xFFC8;
+    public static final int JPG0  = 0xFFF0;
+    public static final int JPG13 = 0xFFFD;
+    public static final int RST0  = 0xFFD0;
+    public static final int RST1  = 0xFFD1;
+    public static final int RST2  = 0xFFD2;
+    public static final int RST3  = 0xFFD3;
+    public static final int RST4  = 0xFFD4;
+    public static final int RST5  = 0xFFD5;
+    public static final int RST6  = 0xFFD6;
+    public static final int RST7  = 0xFFD7;
+    public static final int SOF0  = 0xFFC0;
+    public static final int SOF1  = 0xFFC1;
+    public static final int SOF2  = 0xFFC2;
+    public static final int SOF3  = 0xFFC3;
+    public static final int SOF5  = 0xFFC5;
+    public static final int SOF6  = 0xFFC6;
+    public static final int SOF7  = 0xFFC7;
+    public static final int SOF9  = 0xFFC9;
+    public static final int SOF10 = 0xFFCA;
+    public static final int SOF11 = 0xFFCB;
+    public static final int SOF13 = 0xFFCD;
+    public static final int SOF14 = 0xFFCE;
+    public static final int SOF15 = 0xFFCF;
+    public static final int SOI   = 0xFFD8;
+    public static final int SOS   = 0xFFDA;
+    public static final int TEM   = 0xFF01;
+    /* JPEGFrameComponentParameterConstants */
+    public static final int TQI = 0;
+    public static final int HI  = 1;
+    public static final int VI  = 2;
+    public static final int CW  = 3;
+    public static final int CH  = 4;
+    /* JPEGScanComponentParameterConstants */
+    public static final int DC  = 0;
+    public static final int AC  = 1;
+    /* JFIF Component Constants */
+    public static final int ID_Y        = 1 - 1;
+    public static final int ID_CB   = 2 - 1;
+    public static final int ID_CR   = 3 - 1;
+    public static final RGB[] RGB16 = new RGB[] {
+        new RGB(0,0,0),
+        new RGB(0x80,0,0),
+        new RGB(0,0x80,0),
+        new RGB(0x80,0x80,0),
+        new RGB(0,0,0x80),
+        new RGB(0x80,0,0x80),
+        new RGB(0,0x80,0x80),
+        new RGB(0xC0,0xC0,0xC0),
+        new RGB(0x80,0x80,0x80),
+        new RGB(0xFF,0,0),
+        new RGB(0,0xFF,0),
+        new RGB(0xFF,0xFF,0),
+        new RGB(0,0,0xFF),
+        new RGB(0xFF,0,0xFF),
+        new RGB(0,0xFF,0xFF),
+        new RGB(0xFF,0xFF,0xFF),
+    };
+    public static final int[] ExtendTest = {
+        0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 
+        4096, 8192, 16384, 32768, 65536, 131072, 262144
+    };
+    public static final int[] ExtendOffset = new int[] {
+        0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, 
+        -4095, -8191, -16383, -32767, -65535, -131071, -262143
+    };
+    public static final int[] ZigZag8x8 = {
+        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
+    };
+
+    public static final int[] CrRTable, CbBTable, CrGTable, CbGTable;
+    public static final int[] RYTable, GYTable, BYTable,
+        RCbTable, GCbTable, BCbTable, RCrTable, GCrTable, BCrTable, NBitsTable;
+    static {
+        /* Initialize RGB-YCbCr Tables */
+        int [] rYTable = new int[256];
+        int [] gYTable = new int[256];
+        int [] bYTable = new int[256];
+        int [] rCbTable = new int[256];
+        int [] gCbTable = new int[256];
+        int [] bCbTable = new int[256];
+        int [] gCrTable = new int[256];
+        int [] bCrTable = new int[256];
+        for (int i = 0; i < 256; i++) {
+            rYTable[i] = i * 19595;
+            gYTable[i] = i * 38470;
+            bYTable[i] = i * 7471 + 32768;
+            rCbTable[i] = i * -11059;
+            gCbTable[i] = i * -21709;
+            bCbTable[i] = i * 32768 + 8388608;
+            gCrTable[i] = i * -27439;
+            bCrTable[i] = i * -5329;
+        }
+        RYTable = rYTable;
+        GYTable = gYTable;
+        BYTable = bYTable;
+        RCbTable = rCbTable;
+        GCbTable = gCbTable;
+        BCbTable = bCbTable;
+        RCrTable = bCbTable;
+        GCrTable = gCrTable;
+        BCrTable = bCrTable;
+
+        /* Initialize YCbCr-RGB Tables */
+        int [] crRTable = new int[256];
+        int [] cbBTable = new int[256];
+        int [] crGTable = new int[256];
+        int [] cbGTable = new int[256];
+        for (int i = 0; i < 256; i++) {
+            int x2 = 2 * i - 255;
+            crRTable[i] = (45941 * x2 + 32768) >> 16;
+            cbBTable[i] = (58065 * x2 + 32768) >> 16;
+            crGTable[i] = -23401 * x2;
+            cbGTable[i] = -11277 * x2 + 32768;
+        }
+        CrRTable = crRTable;
+        CbBTable = cbBTable;
+        CrGTable = crGTable;
+        CbGTable = cbGTable;
+
+        /* Initialize BitCount Table */
+        int nBits = 1;
+        int power2 = 2;
+        int [] nBitsTable = new int[2048];
+        nBitsTable[0] = 0;
+        for (int i = 1; i < nBitsTable.length; i++) {
+            if (!(i < power2)) {
+                nBits++;
+                power2 *= 2;
+            }
+            nBitsTable[i] = nBits;
+        }
+        NBitsTable = nBitsTable;
+    }
+void compress(ImageData image, byte[] dataYComp, byte[] dataCbComp, byte[] dataCrComp) {
+    int srcWidth = image.width;
+    int srcHeight = image.height;
+    int vhFactor = maxV * maxH;
+    int[] frameComponent;
+    imageComponents = new byte[nComponents][];
+    for (int i = 0; i < nComponents; i++) {
+        frameComponent = frameComponents[componentIds[i]];
+        imageComponents[i] = new byte[frameComponent[CW] * frameComponent[CH]];
+    }
+    frameComponent = frameComponents[componentIds[ID_Y]];
+    for (int yPos = 0; yPos < srcHeight; yPos++) {
+        int srcOfs = yPos * srcWidth;
+        int dstOfs = yPos * frameComponent[CW];
+        System.arraycopy(dataYComp, srcOfs, imageComponents[ID_Y], dstOfs, srcWidth);
+    }
+    frameComponent = frameComponents[componentIds[ID_CB]];
+    for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+        int destRowIndex = yPos * frameComponent[CW];
+        for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+            int sum = 0;
+            for (int iv = 0; iv < maxV; iv++) {
+                int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+                for (int ih = 0; ih < maxH; ih++) {
+                    sum += dataCbComp[srcIndex + ih] & 0xFF;
+                }
+            }
+            imageComponents[ID_CB][destRowIndex + xPos] = (byte)(sum / vhFactor);
+        }
+    }
+    frameComponent = frameComponents[componentIds[ID_CR]];
+    for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+        int destRowIndex = yPos * frameComponent[CW];
+        for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+            int sum = 0;
+            for (int iv = 0; iv < maxV; iv++) {
+                int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+                for (int ih = 0; ih < maxH; ih++) {
+                    sum += dataCrComp[srcIndex + ih] & 0xFF;
+                }
+            }
+            imageComponents[ID_CR][destRowIndex + xPos] = (byte)(sum / vhFactor);
+        }
+    }
+    for (int iComp = 0; iComp < nComponents; iComp++) {
+        byte[] imageComponent = imageComponents[iComp];
+        frameComponent = frameComponents[componentIds[iComp]];
+        int hFactor = frameComponent[HI];
+        int vFactor = frameComponent[VI];
+        int componentWidth = frameComponent[CW];
+        int componentHeight = frameComponent[CH];
+        int compressedWidth = srcWidth / (maxH / hFactor);
+        int compressedHeight = srcHeight / (maxV / vFactor);
+        if (compressedWidth < componentWidth) {
+            int delta = componentWidth - compressedWidth;
+            for (int yPos = 0; yPos < compressedHeight; yPos++) {
+                int dstOfs = ((yPos + 1) * componentWidth - delta);
+                int dataValue = imageComponent[(dstOfs > 0) ? dstOfs - 1 : 0] & 0xFF;
+                for (int i = 0; i < delta; i++) {
+                    imageComponent[dstOfs + i] = (byte)dataValue;
+                }
+            }
+        }
+        if (compressedHeight < componentHeight) {
+            int srcOfs = (compressedHeight > 0) ? (compressedHeight - 1) * componentWidth : 1;
+            for (int yPos = (compressedHeight > 0) ? compressedHeight : 1; yPos <= componentHeight; yPos++) {
+                int dstOfs = (yPos - 1) * componentWidth;
+                System.arraycopy(imageComponent, srcOfs, imageComponent, dstOfs, componentWidth);
+            }
+        }
+    }
+}
+void convert4BitRGBToYCbCr(ImageData image) {
+    RGB[] rgbs = image.getRGBs();
+    int paletteSize = rgbs.length;
+    byte[] yComp = new byte[paletteSize];
+    byte[] cbComp = new byte[paletteSize];
+    byte[] crComp = new byte[paletteSize];
+    int srcWidth = image.width;
+    int srcHeight = image.height;
+    for (int i = 0; i < paletteSize; i++) {
+        RGB color = rgbs[i];
+        int r = color.red;
+        int g = color.green;
+        int b = color.blue;
+        int n = RYTable[r] + GYTable[g] + BYTable[b];
+        yComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) yComp[i]--;
+        n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+        cbComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) cbComp[i]--;
+        n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+        crComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) crComp[i]--;
+    }
+    int bSize = srcWidth * srcHeight;
+    byte[] dataYComp = new byte[bSize];
+    byte[] dataCbComp = new byte[bSize];
+    byte[] dataCrComp = new byte[bSize];
+    byte[] origData = image.data;
+    int bytesPerLine = image.bytesPerLine;
+    int maxScanlineByte = srcWidth >> 1;
+    for (int yPos = 0; yPos < srcHeight; yPos++) {
+        for (int xPos = 0; xPos < maxScanlineByte; xPos++) {
+            int srcIndex = yPos * bytesPerLine + xPos;
+            int dstIndex = yPos * srcWidth + (xPos * 2);
+            int value2 = origData[srcIndex] & 0xFF;
+            int value1 = value2 >> 4;
+            value2 &= 0x0F;
+            dataYComp[dstIndex] = yComp[value1];
+            dataCbComp[dstIndex] = cbComp[value1];
+            dataCrComp[dstIndex] = crComp[value1];
+            dataYComp[dstIndex + 1] = yComp[value2];
+            dataCbComp[dstIndex + 1] = cbComp[value2];
+            dataCrComp[dstIndex + 1] = crComp[value2];
+        }
+    }
+    compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+void convert8BitRGBToYCbCr(ImageData image) {
+    RGB[] rgbs = image.getRGBs();
+    int paletteSize = rgbs.length;
+    byte[] yComp = new byte[paletteSize];
+    byte[] cbComp = new byte[paletteSize];
+    byte[] crComp = new byte[paletteSize];
+    int srcWidth = image.width;
+    int srcHeight = image.height;
+    for (int i = 0; i < paletteSize; i++) {
+        RGB color = rgbs[i];
+        int r = color.red;
+        int g = color.green;
+        int b = color.blue;
+        int n = RYTable[r] + GYTable[g] + BYTable[b];
+        yComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) yComp[i]--;
+        n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+        cbComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) cbComp[i]--;
+        n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+        crComp[i] = (byte)(n >> 16);
+        if ((n < 0) && ((n & 0xFFFF) !is 0)) crComp[i]--;
+    }
+    int dstWidth = image.width;
+    int dstHeight = srcHeight;
+    int stride = ((srcWidth + 3) >> 2) << 2;
+    int bSize = dstWidth * dstHeight;
+    byte[] dataYComp = new byte[bSize];
+    byte[] dataCbComp = new byte[bSize];
+    byte[] dataCrComp = new byte[bSize];
+    byte[] origData = image.data;
+    for (int yPos = 0; yPos < srcHeight; yPos++) {
+        int srcRowIndex = yPos * stride;
+        int dstRowIndex = yPos * dstWidth;
+        for (int xPos = 0; xPos < srcWidth; xPos++) {
+            int value = origData[srcRowIndex + xPos] & 0xFF;
+            int dstIndex = dstRowIndex + xPos;
+            dataYComp[dstIndex] = yComp[value];
+            dataCbComp[dstIndex] = cbComp[value];
+            dataCrComp[dstIndex] = crComp[value];
+        }
+    }
+    compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+byte[] convertCMYKToRGB() {
+    /* Unsupported CMYK format. Answer an empty byte array. */
+    return new byte[0];
+}
+void convertImageToYCbCr(ImageData image) {
+    switch (image.depth) {
+        case 4:
+            convert4BitRGBToYCbCr(image);
+            return;
+        case 8:
+            convert8BitRGBToYCbCr(image);
+            return;
+        case 16:
+        case 24:
+        case 32:
+            convertMultiRGBToYCbCr(image);
+            return;
+        default:
+            DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+    }
+    return;
+}
+void convertMultiRGBToYCbCr(ImageData image) {
+    int srcWidth = image.width;
+    int srcHeight = image.height;
+    int bSize = srcWidth * srcHeight;
+    byte[] dataYComp = new byte[bSize];
+    byte[] dataCbComp = new byte[bSize];
+    byte[] dataCrComp = new byte[bSize];
+    PaletteData palette = image.palette;
+    int[] buffer = new int[srcWidth];
+    if (palette.isDirect) {
+        int redMask = palette.redMask;
+        int greenMask = palette.greenMask;
+        int blueMask = palette.blueMask;
+        int redShift = palette.redShift;
+        int greenShift = palette.greenShift;
+        int blueShift = palette.blueShift;
+        for (int yPos = 0; yPos < srcHeight; yPos++) {
+            image.getPixels(0, yPos, srcWidth, buffer, 0);
+            int dstRowIndex = yPos * srcWidth;
+            for (int xPos = 0; xPos < srcWidth; xPos++) {
+                int pixel = buffer[xPos];
+                int dstDataIndex = dstRowIndex + xPos;
+                int r = pixel & redMask;
+                r = (redShift < 0) ? r >>> -redShift : r << redShift;
+                int g = pixel & greenMask;
+                g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
+                int b = pixel & blueMask;
+                b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;                
+                dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) >> 16);
+                dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) >> 16);
+                dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) >> 16);
+            }
+        }
+    } else {
+        for (int yPos = 0; yPos < srcHeight; yPos++) {
+            image.getPixels(0, yPos, srcWidth, buffer, 0);
+            int dstRowIndex = yPos * srcWidth;
+            for (int xPos = 0; xPos < srcWidth; xPos++) {
+                int pixel = buffer[xPos];
+                int dstDataIndex = dstRowIndex + xPos;
+                RGB rgb = palette.getRGB(pixel);
+                int r = rgb.red;
+                int g = rgb.green;
+                int b = rgb.blue;
+                dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) >> 16);
+                dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) >> 16);
+                dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) >> 16);
+            }
+        }
+    }
+    compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+byte[] convertYToRGB() {
+    int compWidth = frameComponents[componentIds[ID_Y]][CW];
+    int bytesPerLine = (((imageWidth * 8 + 7) / 8) + 3) / 4 * 4;
+    byte[] data = new byte[bytesPerLine * imageHeight];
+    byte[] yComp = imageComponents[ID_Y];
+    int destIndex = 0;
+    for (int i = 0; i < imageHeight; i++) {
+        int srcIndex = i * compWidth;
+        for (int j = 0; j < bytesPerLine; j++) {
+            int y = yComp[srcIndex] & 0xFF;
+            if (y < 0) {
+                y = 0;
+            } else {
+                if (y > 255) y = 255;
+            }
+            if (j >= imageWidth) {
+                y = 0;
+            }
+            data[destIndex] = (byte)y;
+            srcIndex++;
+            destIndex++;
+        }
+    }
+    return data;
+}
+byte[] convertYCbCrToRGB() {
+    /**
+     * Convert existing image components into an RGB format.
+     * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
+     * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
+     * The conversion equations to be implemented are therefore
+     *  R = Y                + 1.40200 * Cr
+     *  G = Y - 0.34414 * Cb - 0.71414 * Cr
+     *  B = Y + 1.77200 * Cb
+     * where Cb and Cr represent the incoming values less MAXJSAMPLE/2.
+     * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
+     * 
+     * To avoid floating-point arithmetic, we represent the fractional constants
+     * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
+     * the products by 2^16, with appropriate rounding, to get the correct answer.
+     * Notice that Y, being an integral input, does not contribute any fraction
+     * so it need not participate in the rounding.
+     * 
+     * For even more speed, we avoid doing any multiplications in the inner loop
+     * by precalculating the constants times Cb and Cr for all possible values.
+     * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
+     * for 12-bit samples it is still acceptable.  It's not very reasonable for
+     * 16-bit samples, but if you want lossless storage you shouldn't be changing
+     * colorspace anyway.
+     * The Cr=>R and Cb=>B values can be rounded to integers in advance; the
+     * values for the G calculation are left scaled up, since we must add them
+     * together before rounding.
+     */
+    int bSize = imageWidth * imageHeight * nComponents;
+    byte[] rgbData = new byte[bSize];
+    int destIndex = 0;
+    expandImageComponents();
+    byte[] yComp = imageComponents[ID_Y];
+    byte[] cbComp = imageComponents[ID_CB];
+    byte[] crComp = imageComponents[ID_CR];
+    int compWidth = frameComponents[componentIds[ID_Y]][CW];
+    for (int v = 0; v < imageHeight; v++) {
+        int srcIndex = v * compWidth;
+        for (int i = 0; i < imageWidth; i++) {
+            int y = yComp[srcIndex] & 0xFF;
+            int cb = cbComp[srcIndex] & 0xFF;
+            int cr = crComp[srcIndex] & 0xFF;
+            int r = y + CrRTable[cr];
+            int g = y + ((CbGTable[cb] + CrGTable[cr]) >> 16);
+            int b = y + CbBTable[cb];
+            if (r < 0) {
+                r = 0;
+            } else {
+                if (r > 255) r = 255;
+            }
+            if (g < 0) {
+                g = 0;
+            } else {
+                if (g > 255) g = 255;
+            }
+            if (b < 0) {
+                b = 0;
+            } else {
+                if (b > 255) b = 255;
+            }
+            rgbData[destIndex] = (byte)b;
+            rgbData[destIndex + 1] = (byte)g;
+            rgbData[destIndex + 2] = (byte)r;
+            destIndex += 3;
+            srcIndex++;
+        }
+    }
+    return rgbData;
+}
+void decodeACCoefficients(int[] dataUnit, int iComp) {
+    int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+    JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+    int k = 1;
+    while (k < 64) {
+        int rs = decodeUsingTable(acTable);
+        int r = rs >> 4;
+        int s = rs & 0xF;
+        if (s is 0) {
+            if (r is 15) {
+                k += 16;
+            } else {
+                break;
+            }
+        } else {
+            k += r;
+            int bits = receive(s);
+            dataUnit[ZigZag8x8[k]] = extendBy(bits, s);
+            k++;
+        }
+    }
+}
+void decodeACFirstCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+    if (eobrun > 0) {
+        eobrun--;
+        return;
+    }
+    int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+    JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+    int k = start;
+    while (k <= end) {
+        int rs = decodeUsingTable(acTable);
+        int r = rs >> 4;
+        int s = rs & 0xF;
+        if (s is 0) {
+            if (r is 15) {
+                k += 16;
+            } else {
+                eobrun = (1 << r) + receive(r) - 1;
+                break;
+            }
+        } else {
+            k += r;
+            int bits = receive(s);
+            dataUnit[ZigZag8x8[k]] = extendBy(bits, s) << approxBit;
+            k++;
+        }
+    }
+}
+void decodeACRefineCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+    int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+    JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+    int k = start;
+    while (k <= end) {
+        if (eobrun > 0) {
+            while (k <= end) {
+                int zzIndex = ZigZag8x8[k];
+                if (dataUnit[zzIndex] !is 0) {
+                    dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                }
+                k++;
+            }
+            eobrun--;
+        } else {
+            int rs = decodeUsingTable(acTable);
+            int r = rs >> 4;
+            int s = rs & 0xF;
+            if (s is 0) {
+                if (r is 15) {
+                    int zeros = 0;
+                    while (zeros < 16 && k <= end) {
+                        int zzIndex = ZigZag8x8[k];
+                        if (dataUnit[zzIndex] !is 0) {
+                            dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                        } else {
+                            zeros++;
+                        }
+                        k++;
+                    }
+                } else {
+                    eobrun = (1 << r) + receive(r);
+                }
+            } else {
+                int bit = receive(s);
+                int zeros = 0;
+                int zzIndex = ZigZag8x8[k];
+                while ((zeros < r || dataUnit[zzIndex] !is 0) && k <= end) {
+                    if (dataUnit[zzIndex] !is 0) {
+                        dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+                    } else {
+                        zeros++;
+                    }
+                    k++;
+                    zzIndex = ZigZag8x8[k];
+                }
+                if (bit !is 0) {
+                    dataUnit[zzIndex] = 1 << approxBit;
+                } else {
+                    dataUnit[zzIndex] = -1 << approxBit;
+                }
+                k++;
+            }
+        }
+    }
+}
+int refineAC(int ac, int approxBit) {
+    if (ac > 0) {
+        int bit = nextBit();
+        if (bit !is 0) {
+            ac += 1 << approxBit;
+        }
+    } else if (ac < 0) {
+        int bit = nextBit();
+        if (bit !is 0) {
+            ac += -1 << approxBit;
+        }
+    }
+    return ac;
+}
+void decodeDCCoefficient(int[] dataUnit, int iComp, bool first, int approxBit) {
+    int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+    JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+    int lastDC = 0;
+    if (progressive && !first) {
+        int bit = nextBit();
+        lastDC = dataUnit[0] + (bit << approxBit);
+    } else {
+        lastDC = precedingDCs[iComp];
+        int nBits = decodeUsingTable(dcTable);
+        if (nBits !is 0) {
+            int bits = receive(nBits);
+            int diff = extendBy(bits, nBits);
+            lastDC += diff;
+            precedingDCs[iComp] = lastDC;
+        }
+        if (progressive) {
+            lastDC = lastDC << approxBit;
+        }
+    }
+    dataUnit[0] = lastDC;
+}
+void dequantize(int[] dataUnit, int iComp) {
+    int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+    for (int i = 0; i < dataUnit.length; i++) {
+        int zzIndex = ZigZag8x8[i];
+        dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
+    }
+}
+byte[] decodeImageComponents() {
+    if (nComponents is 3) { // compIds 1, 2, 3
+        return convertYCbCrToRGB();
+    }
+//  if (nComponents is 3) { // compIds 1, 4, 5
+//      Unsupported CMYK format.
+//      return convertYIQToRGB();
+//  }
+    if (nComponents is 4) {
+        return convertCMYKToRGB();
+    }
+    return convertYToRGB();
+}
+void decodeMCUAtXAndY(int xmcu, int ymcu, int nComponentsInScan, bool first, int start, int end, int approxBit) {
+    for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+        int scanComponent = iComp;
+        while (scanHeader.componentParameters[componentIds[scanComponent]] is null) {
+            scanComponent++;
+        }
+        int[] frameComponent = frameComponents[componentIds[scanComponent]];
+        int hi = frameComponent[HI];
+        int vi = frameComponent[VI];
+        if (nComponentsInScan is 1) {
+            hi = 1;
+            vi = 1;
+        }
+        int compWidth = frameComponent[CW];
+        for (int ivi = 0; ivi < vi; ivi++) {
+            for (int ihi = 0; ihi < hi; ihi++) {
+                if (progressive) {
+                    // Progressive: First scan - create a new data unit.
+                    // Subsequent scans - refine the existing data unit.
+                    int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+                    dataUnit = dataUnits[scanComponent][index];
+                    if (dataUnit is null) {
+                        dataUnit = new int[64];
+                        dataUnits[scanComponent][index] = dataUnit;
+                    }
+                } else {
+                    // Sequential: Clear and reuse the data unit buffer.
+                    for (int i = 0; i < dataUnit.length; i++) {
+                        dataUnit[i] = 0;
+                    }
+                }
+                if (!progressive || scanHeader.isDCProgressiveScan()) {
+                    decodeDCCoefficient(dataUnit, scanComponent, first, approxBit);
+                }
+                if (!progressive) {
+                    decodeACCoefficients(dataUnit, scanComponent);
+                } else {
+                    if (scanHeader.isACProgressiveScan()) {
+                        if (first) {
+                            decodeACFirstCoefficients(dataUnit, scanComponent, start, end, approxBit);
+                        } else {
+                            decodeACRefineCoefficients(dataUnit, scanComponent, start, end, approxBit);
+                        }
+                    }
+                    if (loader.hasListeners()) {
+                        // Dequantization, IDCT, up-sampling and color conversion
+                        // are done on a copy of the coefficient data in order to
+                        // display the image incrementally.
+                        int[] temp = dataUnit;
+                        dataUnit = new int[64];
+                        System.arraycopy(temp, 0, dataUnit, 0, 64);
+                    }
+                }
+                if (!progressive || (progressive && loader.hasListeners())) {
+                    dequantize(dataUnit, scanComponent);
+                    inverseDCT(dataUnit);
+                    storeData(dataUnit, scanComponent, xmcu, ymcu, hi, ihi, vi, ivi);
+                }
+            }
+        }
+    }
+}
+void decodeScan() {
+    if (progressive && !scanHeader.verifyProgressiveScan()) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+    int mcuRowsInScan = interleavedMcuRows;
+    int mcusPerRow = interleavedMcuCols;
+    if (nComponentsInScan is 1) {
+        // Non-interleaved.
+        int scanComponent = 0;
+        while (scanHeader.componentParameters[componentIds[scanComponent]] is null) {
+            scanComponent++;
+        }
+        int[] frameComponent = frameComponents[componentIds[scanComponent]];
+        int hi = frameComponent[HI];
+        int vi = frameComponent[VI];
+        int mcuWidth = DCTSIZE * maxH / hi;
+        int mcuHeight = DCTSIZE * maxV / vi;
+        mcusPerRow = (imageWidth + mcuWidth - 1) / mcuWidth;
+        mcuRowsInScan = (imageHeight + mcuHeight - 1) / mcuHeight;
+    }
+    bool first = scanHeader.isFirstScan();
+    int start = scanHeader.getStartOfSpectralSelection();
+    int end = scanHeader.getEndOfSpectralSelection();
+    int approxBit = scanHeader.getApproxBitPositionLow();
+    restartsToGo = restartInterval;
+    nextRestartNumber = 0;
+    for (int ymcu = 0; ymcu < mcuRowsInScan; ymcu++) {
+        for (int xmcu = 0; xmcu < mcusPerRow; xmcu++) {
+            if (restartInterval !is 0) {
+                if (restartsToGo is 0) processRestartInterval();
+                restartsToGo--;
+            }
+            decodeMCUAtXAndY(xmcu, ymcu, nComponentsInScan, first, start, end, approxBit);
+        }
+    }
+}
+int decodeUsingTable(JPEGHuffmanTable huffmanTable) {
+    int i = 0;
+    int[] maxCodes = huffmanTable.getDhMaxCodes();
+    int[] minCodes = huffmanTable.getDhMinCodes();
+    int[] valPtrs = huffmanTable.getDhValPtrs();
+    int[] huffVals = huffmanTable.getDhValues();
+    int code = nextBit();
+    while (code > maxCodes[i]) {
+        code = code * 2 + nextBit();
+        i++;
+    }
+    int j = valPtrs[i] + code - minCodes[i];
+    return huffVals[j];
+}
+void emit(int huffCode, int nBits) {
+    if (nBits is 0) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    int[] power2m1 = new int[] {
+        1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 
+        16383, 32767, 65535, 131125
+    };
+    int code = (huffCode & power2m1[nBits - 1]) << (24 - nBits - currentBitCount);
+    byte[] codeBuffer = new byte[4];
+    codeBuffer[0] = (byte)(code & 0xFF);
+    codeBuffer[1] = (byte)((code >> 8) & 0xFF);
+    codeBuffer[2] = (byte)((code >> 16) & 0xFF);
+    codeBuffer[3] = (byte)((code >> 24) & 0xFF);
+    int abs = nBits - (8 - currentBitCount);
+    if (abs < 0) abs = -abs;
+    if ((abs >> 3) > 0) {
+        currentByte += codeBuffer[2];
+        emitByte((byte)currentByte);
+        emitByte(codeBuffer[1]);
+        currentByte = codeBuffer[0];
+        currentBitCount += nBits - 16;
+    } else {
+        currentBitCount += nBits;
+        if (currentBitCount >= 8) {
+            currentByte += codeBuffer[2];
+            emitByte((byte)currentByte);
+            currentByte = codeBuffer[1];
+            currentBitCount -= 8;
+        } else {
+            currentByte += codeBuffer[2];
+        }
+    }
+}
+void emitByte(byte byteValue) {
+    if (bufferCurrentPosition >= 512) {
+        resetOutputBuffer();
+    }
+    dataBuffer[bufferCurrentPosition] = byteValue;
+    bufferCurrentPosition++;
+    if (byteValue is -1) {
+        emitByte((byte)0);
+    }
+}
+void encodeACCoefficients(int[] dataUnit, int iComp) {
+    int[] sParams = scanHeader.componentParameters[iComp];
+    JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+    int[] ehCodes = acTable.ehCodes;
+    byte[] ehSizes = acTable.ehCodeLengths;
+    int r = 0;
+    int k = 1;
+    while (k < 64) {
+        k++;
+        int acValue = dataUnit[ZigZag8x8[k - 1]];
+        if (acValue is 0) {
+            if (k is 64) {
+                emit(ehCodes[0], ehSizes[0] & 0xFF);
+            } else {
+                r++;
+            }
+        } else {
+            while (r > 15) {
+                emit(ehCodes[0xF0], ehSizes[0xF0] & 0xFF);
+                r -= 16;
+            }
+            if (acValue < 0) {
+                int absACValue = acValue;
+                if (absACValue < 0) absACValue = -absACValue;
+                int nBits = NBitsTable[absACValue];
+                int rs = r * 16 + nBits;
+                emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+                emit(0xFFFFFF - absACValue, nBits);
+            } else {
+                int nBits = NBitsTable[acValue];
+                int rs = r * 16 + nBits;
+                emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+                emit(acValue, nBits);
+            }
+            r = 0;
+        }
+    }
+}
+void encodeDCCoefficients(int[] dataUnit, int iComp) {
+    int[] sParams = scanHeader.componentParameters[iComp];
+    JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+    int lastDC = precedingDCs[iComp];
+    int dcValue = dataUnit[0];
+    int diff = dcValue - lastDC;
+    precedingDCs[iComp] = dcValue;
+    if (diff < 0) {
+        int absDiff = 0 - diff;
+        int nBits = NBitsTable[absDiff];
+        emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+        emit(0xFFFFFF - absDiff, nBits);
+    } else {
+        int nBits = NBitsTable[diff];
+        emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+        if (nBits !is 0) {
+            emit(diff, nBits);
+        }
+    }
+}
+void encodeMCUAtXAndY(int xmcu, int ymcu) {
+    int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+    dataUnit = new int[64];
+    for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+        int[] frameComponent = frameComponents[componentIds[iComp]];
+        int hi = frameComponent[HI];
+        int vi = frameComponent[VI];
+        for (int ivi = 0; ivi < vi; ivi++) {
+            for (int ihi = 0; ihi < hi; ihi++) {
+                extractData(dataUnit, iComp, xmcu, ymcu, ihi, ivi);
+                forwardDCT(dataUnit);
+                quantizeData(dataUnit, iComp);
+                encodeDCCoefficients(dataUnit, iComp);
+                encodeACCoefficients(dataUnit, iComp);
+            }
+        }
+    }
+}
+void encodeScan() {
+    for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+        for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+            encodeMCUAtXAndY(xmcu, ymcu);
+        }
+    }
+    if (currentBitCount !is 0) {
+        emitByte((byte)currentByte);
+    }
+    resetOutputBuffer();
+}
+void expandImageComponents() {
+    for (int iComp = 0; iComp < nComponents; iComp++) {
+        int[] frameComponent = frameComponents[componentIds[iComp]];
+        int hi = frameComponent[HI];
+        int vi = frameComponent[VI];
+        int upH = maxH / hi;
+        int upV = maxV / vi;
+        if ((upH * upV) > 1) {
+            byte[] component = imageComponents[iComp];
+            int compWidth = frameComponent[CW];
+            int compHeight = frameComponent[CH];
+            int upCompWidth = compWidth * upH;
+            int upCompHeight = compHeight * upV;
+            ImageData src = new ImageData(compWidth, compHeight, 8, new PaletteData(RGB16), 4, component);
+            ImageData dest = src.scaledTo(upCompWidth, upCompHeight);
+            imageComponents[iComp] = dest.data;
+        }
+    }
+}
+int extendBy(int diff, int t) {
+    if (diff < ExtendTest[t]) {
+        return diff + ExtendOffset[t];
+    } else {
+        return diff;
+    }
+}
+void extractData(int[] dataUnit, int iComp, int xmcu, int ymcu, int ihi, int ivi) {
+    byte[] compImage = imageComponents[iComp];
+    int[] frameComponent = frameComponents[componentIds[iComp]];
+    int hi = frameComponent[HI];
+    int vi = frameComponent[VI];
+    int compWidth = frameComponent[CW];
+    int srcIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+    int destIndex = 0;
+    for (int i = 0; i < DCTSIZE; i++) {
+        for (int col = 0; col < DCTSIZE; col++) {
+            dataUnit[destIndex] = (compImage[srcIndex + col] & 0xFF) - 128;
+            destIndex++;
+        }
+        srcIndex += compWidth;
+    }
+}
+void forwardDCT(int[] dataUnit) {
+    for (int row = 0; row < 8; row++) {
+        int rIndex = row * DCTSIZE;
+        int tmp0 = dataUnit[rIndex] + dataUnit[rIndex + 7];
+        int tmp7 = dataUnit[rIndex] - dataUnit[rIndex + 7];
+        int tmp1 = dataUnit[rIndex + 1] + dataUnit[rIndex + 6];
+        int tmp6 = dataUnit[rIndex + 1] - dataUnit[rIndex + 6];
+        int tmp2 = dataUnit[rIndex + 2] + dataUnit[rIndex + 5];
+        int tmp5 = dataUnit[rIndex + 2] - dataUnit[rIndex + 5];
+        int tmp3 = dataUnit[rIndex + 3] + dataUnit[rIndex + 4];
+        int tmp4 = dataUnit[rIndex + 3] - dataUnit[rIndex + 4];
+
+        /**
+         * Even part per LL&M figure 1 --- note that published figure 
+         * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+         */
+        int tmp10 = tmp0 + tmp3;
+        int tmp13 = tmp0 - tmp3;
+        int tmp11 = tmp1 + tmp2;
+        int tmp12 = tmp1 - tmp2;
+
+        dataUnit[rIndex] = (tmp10 + tmp11) * 4;
+        dataUnit[rIndex + 4]  = (tmp10 - tmp11) * 4;
+
+        int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+        int n = z1 + (tmp13 * FIX_0_765366865) + 1024;
+        dataUnit[rIndex + 2] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 2]--;
+        n = z1 + (tmp12 * (0 - FIX_1_847759065)) + 1024;
+        dataUnit[rIndex + 6] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 6]--;
+
+        /**
+         * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+         * cK represents cos(K*pi/16).
+         * i0..i3 in the paper are tmp4..tmp7 here.
+         */
+        z1 = tmp4 + tmp7;
+        int z2 = tmp5 + tmp6;
+        int z3 = tmp4 + tmp6;
+        int z4 = tmp5 + tmp7;
+        int z5 = (z3 + z4) * FIX_1_175875602;   // sqrt(2) * c3
+
+        tmp4 *= FIX_0_298631336;    // sqrt(2) * (-c1+c3+c5-c7)
+        tmp5 *= FIX_2_053119869;    // sqrt(2) * ( c1+c3-c5+c7)
+        tmp6 *= FIX_3_072711026;    // sqrt(2) * ( c1+c3+c5-c7)
+        tmp7 *= FIX_1_501321110;    // sqrt(2) * ( c1+c3-c5-c7)
+        z1 *= 0 - FIX_0_899976223;  // sqrt(2) * (c7-c3)
+        z2 *= 0 - FIX_2_562915447;  // sqrt(2) * (-c1-c3)
+        z3 *= 0 - FIX_1_961570560;  // sqrt(2) * (-c3-c5)
+        z4 *= 0 - FIX_0_390180644;  // sqrt(2) * (c5-c3)
+
+        z3 += z5;
+        z4 += z5;
+
+        n = tmp4 + z1 + z3 + 1024;
+        dataUnit[rIndex + 7] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 7]--;
+        n = tmp5 + z2 + z4 + 1024;
+        dataUnit[rIndex + 5] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 5]--;
+        n = tmp6 + z2 + z3 + 1024;
+        dataUnit[rIndex + 3] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 3]--;
+        n = tmp7 + z1 + z4 + 1024;
+        dataUnit[rIndex + 1] = n >> 11;
+        if ((n < 0) && ((n & 0x07FF) !is 0)) dataUnit[rIndex + 1]--;
+    }
+
+    /**
+     * Pass 2: process columns.
+     * Note that we must descale the results by a factor of 8 is 2**3,
+     * and also undo the PASS1_BITS scaling.
+     */
+    for (int col = 0; col < 8; col++) {
+        int c0 = col;
+        int c1 = col + 8;
+        int c2 = col + 16;
+        int c3 = col + 24;
+        int c4 = col + 32;
+        int c5 = col + 40;
+        int c6 = col + 48;
+        int c7 = col + 56;
+        int tmp0 = dataUnit[c0] + dataUnit[c7];
+        int tmp7 = dataUnit[c0] - dataUnit[c7];
+        int tmp1 = dataUnit[c1] + dataUnit[c6];
+        int tmp6 = dataUnit[c1] - dataUnit[c6];
+        int tmp2 = dataUnit[c2] + dataUnit[c5];
+        int tmp5 = dataUnit[c2] - dataUnit[c5];
+        int tmp3 = dataUnit[c3] + dataUnit[c4];
+        int tmp4 = dataUnit[c3] - dataUnit[c4];
+
+        /**
+         * Even part per LL&M figure 1 --- note that published figure 
+         * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+         */
+        int tmp10 = tmp0 + tmp3;
+        int tmp13 = tmp0 - tmp3;
+        int tmp11 = tmp1 + tmp2;
+        int tmp12 = tmp1 - tmp2;
+
+        int n = tmp10 + tmp11 + 16;
+        dataUnit[c0] = n >> 5;
+        if ((n < 0) && ((n & 0x1F) !is 0)) dataUnit[c0]--;
+        n = tmp10 - tmp11 + 16;
+        dataUnit[c4] = n >> 5;
+        if ((n < 0) && ((n & 0x1F) !is 0)) dataUnit[c4]--;
+
+        int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+        n = z1 + (tmp13 * FIX_0_765366865) + 131072;
+        dataUnit[c2] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c2]--;
+        n = z1 + (tmp12 * (0 - FIX_1_847759065)) + 131072;
+        dataUnit[c6] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c6]--;
+
+        /**
+         * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+         * cK represents cos(K*pi/16).
+         * i0..i3 in the paper are tmp4..tmp7 here.
+         */
+        z1 = tmp4 + tmp7;
+        int z2 = tmp5 + tmp6;
+        int z3 = tmp4 + tmp6;
+        int z4 = tmp5 + tmp7;
+        int z5 = (z3 + z4) * FIX_1_175875602;   // sqrt(2) * c3
+
+        tmp4 *= FIX_0_298631336;    // sqrt(2) * (-c1+c3+c5-c7)
+        tmp5 *= FIX_2_053119869;    // sqrt(2) * ( c1+c3-c5+c7)
+        tmp6 *= FIX_3_072711026;    // sqrt(2) * ( c1+c3+c5-c7)
+        tmp7 *= FIX_1_501321110;    // sqrt(2) * ( c1+c3-c5-c7)
+        z1 *= 0 - FIX_0_899976223;  // sqrt(2) * (c7-c3)
+        z2 *= 0 - FIX_2_562915447;  // sqrt(2) * (-c1-c3)
+        z3 *= 0 - FIX_1_961570560;  // sqrt(2) * (-c3-c5)
+        z4 *= 0 - FIX_0_390180644;  // sqrt(2) * (c5-c3)
+
+        z3 += z5;
+        z4 += z5;
+
+        n = tmp4 + z1 + z3 + 131072;
+        dataUnit[c7] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c7]--;
+        n = tmp5 + z2 + z4 + 131072;
+        dataUnit[c5] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c5]--;
+        n = tmp6 + z2 + z3 + 131072;
+        dataUnit[c3] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c3]--;
+        n = tmp7 + z1 + z4 + 131072;
+        dataUnit[c1] = n >> 18;
+        if ((n < 0) && ((n & 0x3FFFF) !is 0)) dataUnit[c1]--;
+    }
+}
+void getAPP0() {
+    JPEGAppn appn = new JPEGAppn(inputStream);
+    if (!appn.verify()) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+}
+void getCOM() {
+    new JPEGComment(inputStream);
+}
+void getDAC() {
+    new JPEGArithmeticConditioningTable(inputStream);
+}
+void getDHT() {
+    JPEGHuffmanTable dht = new JPEGHuffmanTable(inputStream);
+    if (!dht.verify()) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    if (acHuffmanTables is null) {
+        acHuffmanTables = new JPEGHuffmanTable[4];
+    }
+    if (dcHuffmanTables is null) {
+        dcHuffmanTables = new JPEGHuffmanTable[4];
+    }
+    JPEGHuffmanTable[] dhtTables = dht.getAllTables();
+    for (int i = 0; i < dhtTables.length; i++) {
+        JPEGHuffmanTable dhtTable = dhtTables[i];
+        if (dhtTable.getTableClass() is 0) {
+            dcHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+        } else {
+            acHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+        }
+    }
+}
+void getDNL() {
+    new JPEGRestartInterval(inputStream);
+}
+void getDQT() {
+    JPEGQuantizationTable dqt = new JPEGQuantizationTable(inputStream);
+    int[][] currentTables = quantizationTables;
+    if (currentTables is null) {
+        currentTables = new int[4][];
+    }
+    int[] dqtTablesKeys = dqt.getQuantizationTablesKeys();
+    int[][] dqtTablesValues = dqt.getQuantizationTablesValues();
+    for (int i = 0; i < dqtTablesKeys.length; i++) {
+        int index = dqtTablesKeys[i];
+        currentTables[index] = dqtTablesValues[i];
+    }
+    quantizationTables = currentTables;
+}
+void getDRI() {
+    JPEGRestartInterval dri = new JPEGRestartInterval(inputStream);
+    if (!dri.verify()) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    restartInterval = dri.getRestartInterval();
+}
+void inverseDCT(int[] dataUnit) {
+    for (int row = 0; row < 8; row++) {
+        int rIndex = row * DCTSIZE;
+        /**
+         * Due to quantization, we will usually find that many of the input
+         * coefficients are zero, especially the AC terms.  We can exploit this
+         * by short-circuiting the IDCT calculation for any row in which all
+         * the AC terms are zero.  In that case each output is equal to the
+         * DC coefficient (with scale factor as needed).
+         * With typical images and quantization tables, half or more of the
+         * row DCT calculations can be simplified this way.
+         */
+        if (isZeroInRow(dataUnit, rIndex)) {
+            int dcVal = dataUnit[rIndex] << 2;
+            for (int i = rIndex + 7; i >= rIndex; i--) {
+                dataUnit[i] = dcVal;
+            }
+        } else {
+            /**
+             * Even part: reverse the even part of the forward DCT.
+             * The rotator is sqrt(2)*c(-6).
+             */
+            int z2 = dataUnit[rIndex + 2];
+            int z3 = dataUnit[rIndex + 6];
+            int z1 = (z2 + z3) * FIX_0_541196100;
+            int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+            int tmp3 = z1 + (z2 * FIX_0_765366865);
+            int tmp0 = (dataUnit[rIndex] + dataUnit[rIndex + 4]) << 13;
+            int tmp1 = (dataUnit[rIndex] - dataUnit[rIndex + 4]) << 13;
+            int tmp10 = tmp0 + tmp3;
+            int tmp13 = tmp0 - tmp3;
+            int tmp11 = tmp1 + tmp2;
+            int tmp12 = tmp1 - tmp2;
+            /**
+             * Odd part per figure 8; the matrix is unitary and hence its
+             * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+             */
+            tmp0 = dataUnit[rIndex + 7];
+            tmp1 = dataUnit[rIndex + 5];
+            tmp2 = dataUnit[rIndex + 3];
+            tmp3 = dataUnit[rIndex + 1];
+            z1 = tmp0 + tmp3;
+            z2 = tmp1 + tmp2;
+            z3 = tmp0 + tmp2;
+            int z4 = tmp1 + tmp3;
+            int z5 = (z3 + z4) * FIX_1_175875602; /* sqrt(2) * c3 */
+              
+            tmp0 *= FIX_0_298631336;        /* sqrt(2) * (-c1+c3+c5-c7) */
+            tmp1 *= FIX_2_053119869;        /* sqrt(2) * ( c1+c3-c5+c7) */
+            tmp2 *= FIX_3_072711026;        /* sqrt(2) * ( c1+c3+c5-c7) */
+            tmp3 *= FIX_1_501321110;        /* sqrt(2) * ( c1+c3-c5-c7) */
+            z1 *= 0 - FIX_0_899976223;  /* sqrt(2) * (c7-c3) */
+            z2 *= 0 - FIX_2_562915447;  /* sqrt(2) * (-c1-c3) */
+            z3 *= 0 - FIX_1_961570560;  /* sqrt(2) * (-c3-c5) */
+            z4 *= 0 - FIX_0_390180644;  /* sqrt(2) * (c5-c3) */
+
+            z3 += z5;
+            z4 += z5;
+            tmp0 += z1 + z3;
+            tmp1 += z2 + z4;
+            tmp2 += z2 + z3;
+            tmp3 += z1 + z4;
+
+            dataUnit[rIndex] = (tmp10 + tmp3 + 1024) >> 11;
+            dataUnit[rIndex + 7] = (tmp10 - tmp3 + 1024) >> 11;
+            dataUnit[rIndex + 1] = (tmp11 + tmp2 + 1024) >> 11;
+            dataUnit[rIndex + 6] = (tmp11 - tmp2 + 1024) >> 11;
+            dataUnit[rIndex + 2] = (tmp12 + tmp1 + 1024) >> 11;
+            dataUnit[rIndex + 5] = (tmp12 - tmp1 + 1024) >> 11;
+            dataUnit[rIndex + 3] = (tmp13 + tmp0 + 1024) >> 11;
+            dataUnit[rIndex + 4] = (tmp13 - tmp0 + 1024) >> 11;
+         }
+    }
+    /**
+     * Pass 2: process columns.
+     * Note that we must descale the results by a factor of 8 is 2**3,
+     * and also undo the PASS1_BITS scaling.
+     */
+    for (int col = 0; col < 8; col++) {
+        int c0 = col;
+        int c1 = col + 8;
+        int c2 = col + 16;
+        int c3 = col + 24;
+        int c4 = col + 32;
+        int c5 = col + 40;
+        int c6 = col + 48;
+        int c7 = col + 56;
+        if (isZeroInColumn(dataUnit, col)) {
+            int dcVal = (dataUnit[c0] + 16) >> 5;
+            dataUnit[c0] = dcVal;
+            dataUnit[c1] = dcVal;
+            dataUnit[c2] = dcVal;
+            dataUnit[c3] = dcVal;
+            dataUnit[c4] = dcVal;
+            dataUnit[c5] = dcVal;
+            dataUnit[c6] = dcVal;
+            dataUnit[c7] = dcVal;
+        } else {
+            /**
+             * Even part: reverse the even part of the forward DCT.
+             * The rotator is sqrt(2)*c(-6).
+             */
+            int z0 = dataUnit[c0];
+            int z2 = dataUnit[c2];
+            int z3 = dataUnit[c6];
+            int z4 = dataUnit[c4];
+            int z1 = (z2 + z3) * FIX_0_541196100;
+            int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+            int tmp3 = z1 + (z2 * FIX_0_765366865);
+            int tmp0 = (z0 + z4) << 13;
+            int tmp1 = (z0 - z4) << 13;
+            int tmp10 = tmp0 + tmp3;
+            int tmp13 = tmp0 - tmp3;
+            int tmp11 = tmp1 + tmp2;
+            int tmp12 = tmp1 - tmp2;
+            /**
+             * Odd part per figure 8; the matrix is unitary and hence its
+             * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+             */
+            tmp0 = dataUnit[c7];
+            tmp1 = dataUnit[c5];
+            tmp2 = dataUnit[c3];
+            tmp3 = dataUnit[c1];
+            z1 = tmp0 + tmp3;
+            z2 = tmp1 + tmp2;
+            z3 = tmp0 + tmp2;
+            z4 = tmp1 + tmp3;
+            z0 = (z3 + z4) * FIX_1_175875602;   /* sqrt(2) * c3 */
+            
+            tmp0 *= FIX_0_298631336;        /* sqrt(2) * (-c1+c3+c5-c7) */
+            tmp1 *= FIX_2_053119869;        /* sqrt(2) * ( c1+c3-c5+c7) */
+            tmp2 *= FIX_3_072711026;        /* sqrt(2) * ( c1+c3+c5-c7) */
+            tmp3 *= FIX_1_501321110;        /* sqrt(2) * ( c1+c3-c5-c7) */
+            z1 *= 0 - FIX_0_899976223;  /* sqrt(2) * (c7-c3) */
+            z2 *= 0 - FIX_2_562915447;  /* sqrt(2) * (-c1-c3) */
+            z3 *= 0 - FIX_1_961570560;  /* sqrt(2) * (-c3-c5) */
+            z4 *= 0 - FIX_0_390180644;  /* sqrt(2) * (c5-c3) */
+            
+            z3 += z0;
+            z4 += z0;
+            
+            tmp0 += z1 + z3;
+            tmp1 += z2 + z4;
+            tmp2 += z2 + z3;
+            tmp3 += z1 + z4;
+
+            /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+            dataUnit[c0] = (tmp10 + tmp3 + 131072) >> 18;
+            dataUnit[c7] = (tmp10 - tmp3 + 131072) >> 18;
+            dataUnit[c1] = (tmp11 + tmp2 + 131072) >> 18;
+            dataUnit[c6] = (tmp11 - tmp2 + 131072) >> 18;
+            dataUnit[c2] = (tmp12 + tmp1 + 131072) >> 18;
+            dataUnit[c5] = (tmp12 - tmp1 + 131072) >> 18;
+            dataUnit[c3] = (tmp13 + tmp0 + 131072) >> 18;
+            dataUnit[c4] = (tmp13 - tmp0 + 131072) >> 18;
+        }
+    }
+}
+bool isFileFormat(LEDataInputStream stream) {
+    try {
+        JPEGStartOfImage soi = new JPEGStartOfImage(stream);
+        stream.unread(soi.reference);
+        return soi.verify();  // we no longer check for appN
+    } catch (Exception e) {
+        return false;
+    }
+}
+bool isZeroInColumn(int[] dataUnit, int col) {
+    return dataUnit[col + 8] is 0 && dataUnit[col + 16] is 0
+            && dataUnit[col + 24] is 0 && dataUnit[col + 32] is 0
+            && dataUnit[col + 40] is 0 && dataUnit[col + 48] is 0
+            && dataUnit[col + 56] is 0;
+}
+bool isZeroInRow(int[] dataUnit, int rIndex) {
+    return dataUnit[rIndex + 1] is 0 && dataUnit[rIndex + 2] is 0
+            && dataUnit[rIndex + 3] is 0 && dataUnit[rIndex + 4] is 0
+            && dataUnit[rIndex + 5] is 0 && dataUnit[rIndex + 6] is 0
+            && dataUnit[rIndex + 7] is 0;
+}
+ImageData[] loadFromByteStream() {
+    //TEMPORARY CODE
+    if (System.getProperty("dwt.internal.image.JPEGFileFormat_3.2") is null) {
+        return JPEGDecoder.loadFromByteStream(inputStream, loader);
+    }
+    JPEGStartOfImage soi = new JPEGStartOfImage(inputStream);
+    if (!soi.verify()) DWT.error(DWT.ERROR_INVALID_IMAGE);
+    restartInterval = 0;
+
+    /* Process the tables preceding the frame header. */
+    processTables();
+    
+    /* Start of Frame. */
+    frameHeader = new JPEGFrameHeader(inputStream);
+    if (!frameHeader.verify()) DWT.error(DWT.ERROR_INVALID_IMAGE);
+    imageWidth = frameHeader.getSamplesPerLine();
+    imageHeight = frameHeader.getNumberOfLines();
+    maxH = frameHeader.getMaxHFactor();
+    maxV = frameHeader.getMaxVFactor();
+    int mcuWidth = maxH * DCTSIZE;
+    int mcuHeight = maxV * DCTSIZE;
+    interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+    interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+    progressive = frameHeader.isProgressive();
+    samplePrecision = frameHeader.getSamplePrecision();
+    nComponents = frameHeader.getNumberOfImageComponents();
+    frameComponents = frameHeader.componentParameters;
+    componentIds = frameHeader.componentIdentifiers;
+    imageComponents = new byte[nComponents][];
+    if (progressive) {
+        // Progressive jpeg: need to keep all of the data units.
+        dataUnits = new int[nComponents][][];
+    } else {
+        // Sequential jpeg: only need one data unit.
+        dataUnit = new int[8 * 8];
+    }
+    for (int i = 0; i < nComponents; i++) {
+        int[] frameComponent = frameComponents[componentIds[i]];
+        int bufferSize = frameComponent[CW] * frameComponent[CH];
+        imageComponents[i] = new byte[bufferSize];
+        if (progressive) {
+            dataUnits[i] = new int[bufferSize][];
+        }
+    }
+
+    /* Process the tables preceding the scan header. */
+    processTables();
+    
+    /* Start of Scan. */
+    scanHeader = new JPEGScanHeader(inputStream);
+    if (!scanHeader.verify()) DWT.error(DWT.ERROR_INVALID_IMAGE);
+    
+    /* Process scan(s) and further tables until EOI. */
+    int progressiveScanCount = 0;
+    bool done = false;
+    while(!done) {
+        resetInputBuffer();
+        precedingDCs = new int[4];
+        decodeScan();
+        if (progressive && loader.hasListeners()) {
+            ImageData imageData = createImageData();
+            loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, false));
+            progressiveScanCount++;
+        }
+
+        /* Unread any buffered data before looking for tables again. */
+        int delta = 512 - bufferCurrentPosition - 1;
+        if (delta > 0) {
+            byte[] unreadBuffer = new byte[delta];
+            System.arraycopy(dataBuffer, bufferCurrentPosition + 1, unreadBuffer, 0, delta);
+            try {
+                inputStream.unread(unreadBuffer);
+            } catch (IOException e) {
+                DWT.error(DWT.ERROR_IO, e);
+            }
+        }
+        
+        /* Process the tables preceding the next scan header. */
+        JPEGSegment jpegSegment = processTables();
+        if (jpegSegment is null || jpegSegment.getSegmentMarker() is EOI) {
+            done = true;
+        } else {
+            scanHeader = new JPEGScanHeader(inputStream);
+            if (!scanHeader.verify()) DWT.error(DWT.ERROR_INVALID_IMAGE);
+        }
+    }
+    
+    if (progressive) {
+        for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+            for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+                for (int iComp = 0; iComp < nComponents; iComp++) {
+                    int[] frameComponent = frameComponents[componentIds[iComp]];
+                    int hi = frameComponent[HI];
+                    int vi = frameComponent[VI];
+                    int compWidth = frameComponent[CW];
+                    for (int ivi = 0; ivi < vi; ivi++) {
+                        for (int ihi = 0; ihi < hi; ihi++) {
+                            int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+                            dataUnit = dataUnits[iComp][index];
+                            dequantize(dataUnit, iComp);
+                            inverseDCT(dataUnit);
+                            storeData(dataUnit, iComp, xmcu, ymcu, hi, ihi, vi, ivi);
+                        }
+                    }
+                }
+            }
+        }
+        dataUnits = null; // release memory
+    }
+    ImageData imageData = createImageData();
+    if (progressive && loader.hasListeners()) {
+        loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, true));
+    }
+    return new ImageData[] {imageData};
+}
+ImageData createImageData() {
+    return ImageData.internal_new(
+        imageWidth,
+        imageHeight, 
+        nComponents * samplePrecision,
+        setUpPalette(),
+        nComponents is 1 ? 4 : 1,
+        decodeImageComponents(),
+        0,
+        null,
+        null,
+        -1,
+        -1,
+        DWT.IMAGE_JPEG,
+        0,
+        0,
+        0,
+        0);
+}
+int nextBit() {
+    if (currentBitCount !is 0) {
+        currentBitCount--;
+        currentByte *= 2;
+        if (currentByte > 255) {
+            currentByte -= 256;
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+    bufferCurrentPosition++;
+    if (bufferCurrentPosition >= 512) {
+        resetInputBuffer();
+        bufferCurrentPosition = 0;
+    }
+    currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+    currentBitCount = 8;
+    byte nextByte;
+    if (bufferCurrentPosition is 511) {
+        resetInputBuffer();
+        currentBitCount = 8;
+        nextByte = dataBuffer[0];
+    } else {
+        nextByte = dataBuffer[bufferCurrentPosition + 1];
+    }
+    if (currentByte is 0xFF) {
+        if (nextByte is 0) {
+            bufferCurrentPosition ++;
+            currentBitCount--;
+            currentByte *= 2;
+            if (currentByte > 255) {
+                currentByte -= 256;
+                return 1;
+            } else {
+                return 0;
+            }
+        } else {
+            if ((nextByte & 0xFF) + 0xFF00 is DNL) {
+                getDNL();
+                return 0;
+            } else {
+                DWT.error(DWT.ERROR_INVALID_IMAGE);
+                return 0;
+            }
+        }
+    } else {
+        currentBitCount--;
+        currentByte *= 2;
+        if (currentByte > 255) {
+            currentByte -= 256;
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
+void processRestartInterval() {
+    do {
+        bufferCurrentPosition++;
+        if (bufferCurrentPosition > 511) {
+            resetInputBuffer();
+            bufferCurrentPosition = 0;
+        }
+        currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+    } while (currentByte !is 0xFF);
+    while (currentByte is 0xFF) {
+        bufferCurrentPosition++;
+        if (bufferCurrentPosition > 511) {
+            resetInputBuffer();
+            bufferCurrentPosition = 0;
+        }
+        currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+    }
+    if (currentByte !is ((RST0 + nextRestartNumber) & 0xFF)) {
+        DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    bufferCurrentPosition++;
+    if (bufferCurrentPosition > 511) {
+        resetInputBuffer();
+        bufferCurrentPosition = 0;
+    }
+    currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+    currentBitCount = 8;
+    restartsToGo = restartInterval;
+    nextRestartNumber = (nextRestartNumber + 1) & 0x7;
+    precedingDCs = new int[4];
+    eobrun = 0;
+}
+/* Process all markers until a frame header, scan header, or EOI is found. */
+JPEGSegment processTables() {
+    while (true) {
+        JPEGSegment jpegSegment = seekUnspecifiedMarker(inputStream);
+        if (jpegSegment is null) return null;
+        JPEGFrameHeader sof = new JPEGFrameHeader(jpegSegment.reference);
+        if (sof.verify()) {
+            return jpegSegment;
+        }
+        int marker = jpegSegment.getSegmentMarker();
+        switch (marker) {
+            case SOI: // there should only be one SOI per file
+                DWT.error(DWT.ERROR_INVALID_IMAGE);
+            case EOI:
+            case SOS:
+                return jpegSegment;
+            case DQT:
+                getDQT();
+                break;
+            case DHT:
+                getDHT();
+                break;
+            case DAC:
+                getDAC();
+                break;
+            case DRI:
+                getDRI();
+                break;
+            case APP0:
+                getAPP0();
+                break;
+            case COM:
+                getCOM();
+                break;
+            default:
+                skipSegmentFrom(inputStream);
+            
+        }
+    }
+}
+void quantizeData(int[] dataUnit, int iComp) {
+    int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+    for (int i = 0; i < dataUnit.length; i++) {
+        int zzIndex = ZigZag8x8[i];
+        int data = dataUnit[zzIndex];
+        int absData = data < 0 ? 0 - data : data;
+        int qValue = qTable[i];
+        int q2 = qValue >> 1;
+        absData += q2;
+        if (absData < qValue) {
+            dataUnit[zzIndex] = 0;
+        } else {
+            absData /= qValue;
+            if (data >= 0) {
+                dataUnit[zzIndex] = absData;
+            } else {
+                dataUnit[zzIndex] = 0 - absData;
+            }
+        }
+    }
+}
+int receive(int nBits) {
+    int v = 0;
+    for (int i = 0; i < nBits; i++) {
+        v = v * 2 + nextBit();
+    }
+    return v;
+}
+void resetInputBuffer() {
+    if (dataBuffer is null) {
+        dataBuffer = new byte[512];
+    }
+    try {
+        inputStream.read(dataBuffer);
+    } catch (IOException e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+    currentBitCount = 0;
+    bufferCurrentPosition = -1;
+}
+void resetOutputBuffer() {
+    if (dataBuffer is null) {
+        dataBuffer = new byte[512];
+    } else {
+        try {
+            outputStream.write(dataBuffer, 0, bufferCurrentPosition);
+        } catch (IOException e) {
+            DWT.error(DWT.ERROR_IO, e);
+        }
+    }
+    bufferCurrentPosition = 0;
+}
+static JPEGSegment seekUnspecifiedMarker(LEDataInputStream byteStream) {
+    byte[] byteArray = new byte[2];
+    try {
+        while (true) {
+            if (byteStream.read(byteArray, 0, 1) !is 1) return null;
+            if (byteArray[0] is (byte) 0xFF) {
+                if (byteStream.read(byteArray, 1, 1) !is 1) return null;
+                if (byteArray[1] !is (byte) 0xFF && byteArray[1] !is 0) {
+                    byteStream.unread(byteArray);
+                    return new JPEGSegment(byteArray);
+                }
+            }
+        }
+    } catch (IOException e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+    return null;
+}
+PaletteData setUpPalette() {
+    if (nComponents is 1) {
+        RGB[] entries = new RGB[256];
+        for (int i = 0; i < 256; i++) {
+            entries[i] = new RGB(i, i, i);
+        }
+        return new PaletteData(entries);
+    }
+    return new PaletteData(0xFF, 0xFF00, 0xFF0000);
+}
+static void skipSegmentFrom(LEDataInputStream byteStream) {
+    try {
+        byte[] byteArray = new byte[4];
+        JPEGSegment jpegSegment = new JPEGSegment(byteArray);
+    
+        if (byteStream.read(byteArray) !is byteArray.length) {
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+        }
+        if (!(byteArray[0] is -1 && byteArray[1] !is 0 && byteArray[1] !is -1)) {
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+        }
+        int delta = jpegSegment.getSegmentLength() - 2;
+        byteStream.skip(delta);
+    } catch (Exception e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+}
+void storeData(int[] dataUnit, int iComp, int xmcu, int ymcu, int hi, int ihi, int vi, int ivi) {
+    byte[] compImage = imageComponents[iComp];
+    int[] frameComponent = frameComponents[componentIds[iComp]];
+    int compWidth = frameComponent[CW];
+    int destIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+    int srcIndex = 0;
+    for (int i = 0; i < DCTSIZE; i++) {
+        for (int col = 0; col < DCTSIZE; col++) {
+            int x = dataUnit[srcIndex] + 128;
+            if (x < 0) {
+                x = 0;
+            } else {
+                if (x > 255) x = 255;
+            }
+            compImage[destIndex + col] = (byte)x;
+            srcIndex++;
+        }
+        destIndex += compWidth;
+    }
+}
+void unloadIntoByteStream(ImageLoader loader) {
+    ImageData image = loader.data[0];
+    if (!new JPEGStartOfImage().writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    JPEGAppn appn = new JPEGAppn(new byte[] {(byte)0xFF, (byte)0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0});
+    if (!appn.writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    quantizationTables = new int[4][];
+    JPEGQuantizationTable chromDQT = JPEGQuantizationTable.defaultChrominanceTable();
+    chromDQT.scaleBy(encoderQFactor);
+    int[] jpegDQTKeys = chromDQT.getQuantizationTablesKeys();
+    int[][] jpegDQTValues = chromDQT.getQuantizationTablesValues();
+    for (int i = 0; i < jpegDQTKeys.length; i++) {
+        quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+    }
+    JPEGQuantizationTable lumDQT = JPEGQuantizationTable.defaultLuminanceTable();
+    lumDQT.scaleBy(encoderQFactor);
+    jpegDQTKeys = lumDQT.getQuantizationTablesKeys();
+    jpegDQTValues = lumDQT.getQuantizationTablesValues();
+    for (int i = 0; i < jpegDQTKeys.length; i++) {
+        quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+    }
+    if (!lumDQT.writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    if (!chromDQT.writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    int frameLength, scanLength, precision;
+    int[][] frameParams, scanParams;
+    if (image.depth is 1) {
+        frameLength = 11;
+        frameParams = new int[1][];
+        frameParams[0] = new int[] {1, 1, 1, 0, 0};
+        scanParams = new int[1][];
+        scanParams[0] = new int[] {0, 0};
+        scanLength = 8;
+        nComponents = 1;
+        precision = 1;
+    } else {
+        frameLength = 17;
+        frameParams = new int[3][];
+        frameParams[0] = new int[] {0, 2, 2, 0, 0};
+        frameParams[1] = new int[] {1, 1, 1, 0, 0};
+        frameParams[2] = new int[] {1, 1, 1, 0, 0};
+        scanParams = new int[3][];
+        scanParams[0] = new int[] {0, 0};
+        scanParams[1] = new int[] {1, 1};
+        scanParams[2] = new int[] {1, 1};
+        scanLength = 12;
+        nComponents = 3;
+        precision = 8;
+    }
+    imageWidth = image.width;
+    imageHeight = image.height;
+    frameHeader = new JPEGFrameHeader(new byte[19]);
+    frameHeader.setSegmentMarker(SOF0);
+    frameHeader.setSegmentLength(frameLength);
+    frameHeader.setSamplePrecision(precision);
+    frameHeader.setSamplesPerLine(imageWidth);
+    frameHeader.setNumberOfLines(imageHeight);
+    frameHeader.setNumberOfImageComponents(nComponents);
+    frameHeader.componentParameters = frameParams;
+    frameHeader.componentIdentifiers = new int[] {0, 1, 2};
+    frameHeader.initializeContents();
+    if (!frameHeader.writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    frameComponents = frameParams;
+    componentIds = frameHeader.componentIdentifiers;
+    maxH = frameHeader.getMaxHFactor();
+    maxV = frameHeader.getMaxVFactor();
+    int mcuWidth = maxH * DCTSIZE;
+    int mcuHeight = maxV * DCTSIZE;
+    interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+    interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+    acHuffmanTables = new JPEGHuffmanTable[4];
+    dcHuffmanTables = new JPEGHuffmanTable[4];
+    JPEGHuffmanTable[] dhtTables = new JPEGHuffmanTable[] {
+        JPEGHuffmanTable.getDefaultDCLuminanceTable(),
+        JPEGHuffmanTable.getDefaultDCChrominanceTable(),
+        JPEGHuffmanTable.getDefaultACLuminanceTable(),
+        JPEGHuffmanTable.getDefaultACChrominanceTable()
+    };
+    for (int i = 0; i < dhtTables.length; i++) {
+        JPEGHuffmanTable dhtTable = dhtTables[i];
+        if (!dhtTable.writeToStream(outputStream)) {
+            DWT.error(DWT.ERROR_IO);
+        }
+        JPEGHuffmanTable[] allTables = dhtTable.getAllTables();
+        for (int j = 0; j < allTables.length; j++) {
+            JPEGHuffmanTable huffmanTable = allTables[j];
+            if (huffmanTable.getTableClass() is 0) {
+                dcHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+            } else {
+                acHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+            }
+        }
+    }
+    precedingDCs = new int[4];
+    scanHeader = new JPEGScanHeader(new byte[14]);
+    scanHeader.setSegmentMarker(SOS);
+    scanHeader.setSegmentLength(scanLength);
+    scanHeader.setNumberOfImageComponents(nComponents);
+    scanHeader.setStartOfSpectralSelection(0);
+    scanHeader.setEndOfSpectralSelection(63);
+    scanHeader.componentParameters = scanParams;
+    scanHeader.initializeContents();
+    if (!scanHeader.writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+    convertImageToYCbCr(image);
+    resetOutputBuffer();
+    currentByte = 0;
+    currentBitCount = 0;
+    encodeScan();
+    if (!new JPEGEndOfImage().writeToStream(outputStream)) {
+        DWT.error(DWT.ERROR_IO);
+    }
+}
+}