package org.eclipse.swt.internal.image; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved | |
*/ | |
import org.eclipse.swt.*; | |
import org.eclipse.swt.graphics.*; | |
import java.io.*; | |
final class JPEGFileFormat extends FileFormat { | |
int restartInterval; | |
JPEGFrameHeader frameHeader; | |
int imageWidth, imageHeight; | |
int interleavedMcuCols, interleavedMcuRows; | |
int maxV, maxH; | |
boolean 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; | |
JPEGArithmeticConditioningTable arithmeticTables; | |
JPEGHuffmanTable[] acHuffmanTables; | |
JPEGHuffmanTable[] dcHuffmanTables; | |
int[][] quantizationTables; | |
int currentByte; | |
int decoderQFactor; | |
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 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 int[] CrRTable, CbBTable, CrGTable, CbGTable; | |
public static int[] RYTable, GYTable, BYTable, | |
RCbTable, GCbTable, BCbTable, RCrTable, GCrTable, BCrTable, NBitsTable; | |
static { | |
initialize(); | |
} | |
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 - 1] & 0xFF; | |
for (int i = 0; i < delta; i++) { | |
imageComponent[dstOfs + i] = (byte)dataValue; | |
} | |
} | |
} | |
if (compressedHeight < componentHeight) { | |
int srcOfs = (compressedHeight - 1) * componentWidth; | |
for (int yPos = compressedHeight; 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 / 65536); | |
if ((n < 0) && (n % 65536 != 0)) yComp[i]--; | |
n = RCbTable[r] + GCbTable[g] + BCbTable[b]; | |
cbComp[i] = (byte)(n / 65536); | |
if ((n < 0) && (n % 65536 != 0)) cbComp[i]--; | |
n = RCrTable[r] + GCrTable[g] + BCrTable[b]; | |
crComp[i] = (byte)(n / 65536); | |
if ((n < 0) && (n % 65536 != 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; | |
for (int yPos = 0; yPos < srcHeight; yPos++) { | |
for (int xPos = 0; xPos < srcWidth / 2; xPos++) { | |
int srcIndex = yPos * (srcWidth / 2) + xPos; | |
int dstIndex = yPos * srcWidth + (xPos * 2); | |
int value2 = origData[srcIndex] & 0xFF; | |
int value1 = value2 / 16; | |
value2 = value2 % 16; | |
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 / 65536); | |
if ((n < 0) && (n % 65536 != 0)) yComp[i]--; | |
n = RCbTable[r] + GCbTable[g] + BCbTable[b]; | |
cbComp[i] = (byte)(n / 65536); | |
if ((n < 0) && (n % 65536 != 0)) cbComp[i]--; | |
n = RCrTable[r] + GCrTable[g] + BCrTable[b]; | |
crComp[i] = (byte)(n / 65536); | |
if ((n < 0) && (n % 65536 != 0)) crComp[i]--; | |
} | |
int dstWidth = image.width; | |
int dstHeight = srcHeight; | |
int stride = (srcWidth + 3) / 4 * 4; | |
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: | |
SWT.error(SWT.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]; | |
byte[] origData = image.data; | |
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]) / 65536); | |
dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536); | |
dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536); | |
} | |
} | |
} 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]) / 65536); | |
dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536); | |
dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536); | |
} | |
} | |
} | |
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]) / 65536); | |
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; | |
} | |
byte[] convertYIQToRGB() { | |
/* Unsupported CMYK format. Answer an empty byte array. */ | |
return new byte[0]; | |
} | |
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 == 0) { | |
if (r == 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 == 0) { | |
if (r == 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] != 0) { | |
dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit); | |
} | |
k++; | |
} | |
eobrun--; | |
} else { | |
int rs = decodeUsingTable(acTable); | |
int r = rs >> 4; | |
int s = rs & 0xF; | |
if (s == 0) { | |
if (r == 15) { | |
int zeros = 0; | |
while (zeros < 16 && k <= end) { | |
int zzIndex = ZigZag8x8[k]; | |
if (dataUnit[zzIndex] != 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] != 0) && k <= end) { | |
if (dataUnit[zzIndex] != 0) { | |
dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit); | |
} else { | |
zeros++; | |
} | |
k++; | |
zzIndex = ZigZag8x8[k]; | |
} | |
if (bit != 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 != 0) { | |
ac = ac + (1 << approxBit); | |
} | |
} else if (ac < 0) { | |
int bit = nextBit(); | |
if (bit != 0) { | |
ac = ac + (-1 << approxBit); | |
} | |
} | |
return ac; | |
} | |
void decodeDCCoefficient(int[] dataUnit, int iComp, boolean 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 != 0) { | |
int bits = receive(nBits); | |
int diff = extendBy(bits, nBits); | |
lastDC = 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() { | |
int[] compIds = new int[nComponents]; | |
int compIdsIndex = 0; | |
for (int i = 0; i < nComponents; i++) { | |
compIds[compIdsIndex] = i + 1; | |
compIdsIndex++; | |
} | |
if ((compIds.length == 3) && | |
(compIds[0] == 1) && | |
(compIds[1] == 2) && | |
(compIds[2] == 3)) { | |
return convertYCbCrToRGB(); | |
} | |
if ((compIds.length == 3) && | |
(compIds[0] == 1) && | |
(compIds[1] == 4) && | |
(compIds[2] == 5)) { | |
return convertYIQToRGB(); | |
} | |
if (compIds.length == 4) { | |
return convertCMYKToRGB(); | |
} | |
return convertYToRGB(); | |
} | |
void decodeMCUAtXAndY(int xmcu, int ymcu, int nComponentsInScan, boolean first, int start, int end, int approxBit) { | |
for (int iComp = 0; iComp < nComponentsInScan; iComp++) { | |
int scanComponent = iComp; | |
while (scanHeader.componentParameters[componentIds[scanComponent]] == null) { | |
scanComponent++; | |
} | |
int[] frameComponent = frameComponents[componentIds[scanComponent]]; | |
int hi = frameComponent[HI]; | |
int vi = frameComponent[VI]; | |
if (nComponentsInScan == 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 == 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()) { | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
} | |
int nComponentsInScan = scanHeader.getNumberOfImageComponents(); | |
int mcuRowsInScan = interleavedMcuRows; | |
int mcusPerRow = interleavedMcuCols; | |
if (nComponentsInScan == 1) { | |
// Non-interleaved. | |
int scanComponent = 0; | |
while (scanHeader.componentParameters[componentIds[scanComponent]] == 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; | |
} | |
boolean 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 != 0) { | |
if (restartsToGo == 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]; | |
j = j + code - minCodes[i]; | |
return huffVals[j]; | |
} | |
void emit(int huffCode, int nBits) { | |
if (nBits == 0) { | |
SWT.error(SWT.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 % 256); | |
codeBuffer[1] = (byte)((code / 256) % 256); | |
codeBuffer[2] = (byte)((code / 65536) % 256); | |
codeBuffer[3] = (byte)((code / 16777216) % 256); | |
int abs = nBits - (8 - currentBitCount); | |
if (abs < 0) abs = -abs; | |
if ((abs / 8) > 0) { | |
currentByte += codeBuffer[2]; | |
emitByte((byte)currentByte); | |
emitByte((byte)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 == -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 == 0) { | |
if (k == 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 != 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 != 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; | |
RGB[] rgbs = 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), | |
}; | |
ImageData src = new ImageData(compWidth, compHeight, 8, new PaletteData(rgbs), 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 scaleFactor1 = ExtendTest[11]; | |
int scaleFactor2 = ExtendTest[12]; | |
int n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1; | |
dataUnit[rIndex + 2] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 2]--; | |
n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1; | |
dataUnit[rIndex + 6] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 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 = tmp4 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7) | |
tmp5 = tmp5 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7) | |
tmp6 = tmp6 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7) | |
tmp7 = tmp7 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7) | |
z1 = z1 * (0 - FIX_0_899976223); // sqrt(2) * (c7-c3) | |
z2 = z2 * (0 - FIX_2_562915447); // sqrt(2) * (-c1-c3) | |
z3 = z3 * (0 - FIX_1_961570560); // sqrt(2) * (-c3-c5) | |
z4 = z4 * (0 - FIX_0_390180644); // sqrt(2) * (c5-c3) | |
z3 = z3 + z5; | |
z4 = z4 + z5; | |
n = tmp4 + z1 + z3 + scaleFactor1; | |
dataUnit[rIndex + 7] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 7]--; | |
n = tmp5 + z2 + z4 + scaleFactor1; | |
dataUnit[rIndex + 5] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 5]--; | |
n = tmp6 + z2 + z3 + scaleFactor1; | |
dataUnit[rIndex + 3] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 3]--; | |
n = tmp7 + z1 + z4 + scaleFactor1; | |
dataUnit[rIndex + 1] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 1]--; | |
} | |
/** | |
* Pass 2: process columns. | |
* Note that we must descale the results by a factor of 8 == 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 scaleFactor1 = ExtendTest[5]; | |
int scaleFactor2 = ExtendTest[6]; | |
int n = tmp10 + tmp11 + scaleFactor1; | |
dataUnit[c0] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c0]--; | |
n = tmp10 - tmp11 + scaleFactor1; | |
dataUnit[c4] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c4]--; | |
int z1 = (tmp12 + tmp13) * FIX_0_541196100; | |
scaleFactor1 = ExtendTest[18]; | |
scaleFactor2 = ExtendTest[19]; | |
n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1; | |
dataUnit[c2] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c2]--; | |
n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1; | |
dataUnit[c6] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 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 = tmp4 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7) | |
tmp5 = tmp5 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7) | |
tmp6 = tmp6 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7) | |
tmp7 = tmp7 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7) | |
z1 = z1 * (0 - FIX_0_899976223); // sqrt(2) * (c7-c3) | |
z2 = z2 * (0 - FIX_2_562915447); // sqrt(2) * (-c1-c3) | |
z3 = z3 * (0 - FIX_1_961570560); // sqrt(2) * (-c3-c5) | |
z4 = z4 * (0 - FIX_0_390180644); // sqrt(2) * (c5-c3) | |
z3 = z3 + z5; | |
z4 = z4 + z5; | |
n = tmp4 + z1 + z3 + scaleFactor1; | |
dataUnit[c7] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c7]--; | |
n = tmp5 + z2 + z4 + scaleFactor1; | |
dataUnit[c5] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c5]--; | |
n = tmp6 + z2 + z3 + scaleFactor1; | |
dataUnit[c3] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c3]--; | |
n = tmp7 + z1 + z4 + scaleFactor1; | |
dataUnit[c1] = n / scaleFactor2; | |
if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c1]--; | |
} | |
} | |
void getAPP0() { | |
JPEGAppn appn = new JPEGAppn(inputStream); | |
if (!appn.verify()) { | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
} | |
} | |
void getCOM() { | |
new JPEGComment(inputStream); | |
} | |
void getDAC() { | |
JPEGArithmeticConditioningTable dac = new JPEGArithmeticConditioningTable(inputStream); | |
arithmeticTables = dac; | |
} | |
void getDHT() { | |
JPEGHuffmanTable dht = new JPEGHuffmanTable(inputStream); | |
if (!dht.verify()) { | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
} | |
if (acHuffmanTables == null) { | |
acHuffmanTables = new JPEGHuffmanTable[4]; | |
} | |
if (dcHuffmanTables == null) { | |
dcHuffmanTables = new JPEGHuffmanTable[4]; | |
} | |
JPEGHuffmanTable[] dhtTables = dht.getAllTables(); | |
for (int i = 0; i < dhtTables.length; i++) { | |
JPEGHuffmanTable dhtTable = dhtTables[i]; | |
if (dhtTable.getTableClass() == 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 == 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()) { | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
} | |
restartInterval = dri.getRestartInterval(); | |
} | |
static void initialize() { | |
initializeRGBYCbCrTables(); | |
initializeYCbCrRGBTables(); | |
initializeBitCountTable(); | |
} | |
static void initializeBitCountTable() { | |
int nBits = 1; | |
int count = 0; | |
int power2 = 2; | |
NBitsTable = new int[2048]; | |
NBitsTable[0] = 0; | |
for (int i = 1; i < NBitsTable.length; i++) { | |
if (!(i < power2)) { | |
nBits++; | |
power2 *= 2; | |
} | |
NBitsTable[i] = nBits; | |
} | |
} | |
static void initializeRGBYCbCrTables() { | |
RYTable = new int[256]; | |
GYTable = new int[256]; | |
BYTable = new int[256]; | |
RCbTable = new int[256]; | |
GCbTable = new int[256]; | |
BCbTable = new int[256]; | |
RCrTable = BCbTable; | |
GCrTable = new int[256]; | |
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; | |
} | |
} | |
static void initializeYCbCrRGBTables() { | |
CrRTable = new int[256]; | |
CbBTable = new int[256]; | |
CrGTable = new int[256]; | |
CbGTable = new int[256]; | |
for (int i = 0; i < 256; i++) { | |
int x2 = 2 * i - 255; | |
CrRTable[i] = (45941 * x2 + 32768) / 65536; | |
CbBTable[i] = (58065 * x2 + 32768) / 65536; | |
CrGTable[i] = -23401 * x2; | |
CbGTable[i] = -11277 * x2 + 32768; | |
} | |
} | |
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] * 4; | |
for (int i = rIndex; i < rIndex + 8; 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]) * 8192; | |
int tmp1 = (dataUnit[rIndex] - dataUnit[rIndex + 4]) * 8192; | |
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 = tmp0 * FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ | |
tmp1 = tmp1 * FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ | |
tmp2 = tmp2 * FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ | |
tmp3 = tmp3 * FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ | |
z1 = z1 * (0 - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ | |
z2 = z2 * (0 - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ | |
z3 = z3 * (0 - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ | |
z4 = z4 * (0 - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ | |
z3 = z3 + z5; | |
z4 = z4 + z5; | |
tmp0 = tmp0 + z1 + z3; | |
tmp1 = tmp1 + z2 + z4; | |
tmp2 = tmp2 + z2 + z3; | |
tmp3 = tmp3 + z1 + z4; | |
int descaleFactor1 = ExtendTest[11]; | |
int descaleFactor2 = ExtendTest[12]; | |
dataUnit[rIndex] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2; | |
dataUnit[rIndex + 4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2; | |
} | |
} | |
/** | |
* Pass 2: process columns. | |
* Note that we must descale the results by a factor of 8 == 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) / 32; | |
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 z2 = dataUnit[c2]; | |
int z3 = dataUnit[c6]; | |
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[c0] + dataUnit[c4]) * 8192; | |
int tmp1 = (dataUnit[c0] - dataUnit[c4]) * 8192; | |
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; | |
int z4 = tmp1 + tmp3; | |
int z5 = (z3 + z4) * FIX_1_175875602; /* sqrt(2) * c3 */ | |
tmp0 = tmp0 * FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ | |
tmp1 = tmp1 * FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ | |
tmp2 = tmp2 * FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ | |
tmp3 = tmp3 * FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ | |
z1 = z1 * (0 - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ | |
z2 = z2 * (0 - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ | |
z3 = z3 * (0 - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ | |
z4 = z4 * (0 - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ | |
z3 = z3 + z5; | |
z4 = z4 + z5; | |
tmp0 = tmp0 + z1 + z3; | |
tmp1 = tmp1 + z2 + z4; | |
tmp2 = tmp2 + z2 + z3; | |
tmp3 = tmp3 + z1 + z4; | |
/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ | |
int descaleFactor1 = ExtendTest[18]; | |
int descaleFactor2 = ExtendTest[19]; | |
dataUnit[c0] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2; | |
dataUnit[c7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2; | |
dataUnit[c1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2; | |
dataUnit[c6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2; | |
dataUnit[c2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2; | |
dataUnit[c5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2; | |
dataUnit[c3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2; | |
dataUnit[c4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2; | |
} | |
} | |
} | |
public static boolean isJPEGFile(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; | |
} | |
} | |
boolean isZeroInColumn(int[] dataUnit, int col) { | |
return (dataUnit[col + 8] + dataUnit[col + 16] + | |
dataUnit[col + 24] + dataUnit[col + 32] + | |
dataUnit[col + 40] + dataUnit[col + 48] + | |
dataUnit[col + 56]) == 0; | |
} | |
boolean isZeroInRow(int[] dataUnit, int rIndex) { | |
return (dataUnit[rIndex + 1] + dataUnit[rIndex + 2] + | |
dataUnit[rIndex + 3] + dataUnit[rIndex + 4] + | |
dataUnit[rIndex + 5] + dataUnit[rIndex + 6] + | |
dataUnit[rIndex + 7]) == 0; | |
} | |
ImageData[] loadFromByteStream() { | |
JPEGStartOfImage soi = new JPEGStartOfImage(inputStream); | |
if (!soi.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
restartInterval = 0; | |
/* Process the tables preceding the frame header. */ | |
processTables(); | |
/* Start of Frame. */ | |
frameHeader = new JPEGFrameHeader(inputStream); | |
if (!frameHeader.verify()) SWT.error(SWT.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()) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
/* Process scan(s) and further tables until EOI. */ | |
int progressiveScanCount = 0; | |
boolean 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) { | |
SWT.error(SWT.ERROR_IO, e); | |
} | |
} | |
/* Process the tables preceding the next scan header. */ | |
JPEGSegment jpegSegment = processTables(); | |
if (jpegSegment == null || jpegSegment.getSegmentMarker() == EOI) { | |
done = true; | |
} else { | |
scanHeader = new JPEGScanHeader(inputStream); | |
if (!scanHeader.verify()) SWT.error(SWT.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); | |
} | |
} | |
} | |
} | |
} | |
} | |
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 == 1 ? 4 : 1, | |
decodeImageComponents(), | |
0, | |
null, | |
null, | |
-1, | |
-1, | |
SWT.IMAGE_JPEG, | |
0, | |
0, | |
0, | |
0); | |
} | |
int nextBit() { | |
if (currentBitCount != 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 == 511) { | |
resetInputBuffer(); | |
currentBitCount = 8; | |
nextByte = dataBuffer[0]; | |
} else { | |
nextByte = dataBuffer[bufferCurrentPosition + 1]; | |
} | |
if (currentByte == 0xFF) { | |
if (nextByte == 0) { | |
bufferCurrentPosition ++; | |
currentBitCount--; | |
currentByte *= 2; | |
if (currentByte > 255) { | |
currentByte -= 256; | |
return 1; | |
} else { | |
return 0; | |
} | |
} else { | |
if ((nextByte & 0xFF) + 0xFF00 == DNL) { | |
getDNL(); | |
return 0; | |
} else { | |
SWT.error(SWT.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 != 0xFF); | |
while (currentByte == 0xFF) { | |
bufferCurrentPosition++; | |
if (bufferCurrentPosition > 511) { | |
resetInputBuffer(); | |
bufferCurrentPosition = 0; | |
} | |
currentByte = dataBuffer[bufferCurrentPosition] & 0xFF; | |
} | |
if (currentByte != ((RST0 + nextRestartNumber) % 256)) { | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
} | |
bufferCurrentPosition++; | |
if (bufferCurrentPosition > 511) { | |
resetInputBuffer(); | |
bufferCurrentPosition = 0; | |
} | |
currentByte = dataBuffer[bufferCurrentPosition] & 0xFF; | |
currentBitCount = 8; | |
restartsToGo = restartInterval; | |
nextRestartNumber = (nextRestartNumber + 1) % 8; | |
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 == 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 | |
SWT.error(SWT.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 / 2; | |
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 == null) { | |
dataBuffer = new byte[512]; | |
} | |
try { | |
inputStream.read(dataBuffer); | |
} catch (IOException e) { | |
SWT.error(SWT.ERROR_IO, e); | |
} | |
currentBitCount = 0; | |
bufferCurrentPosition = -1; | |
} | |
void resetOutputBuffer() { | |
if (dataBuffer == null) { | |
dataBuffer = new byte[512]; | |
} else { | |
try { | |
outputStream.write(dataBuffer, 0, bufferCurrentPosition); | |
} catch (IOException e) { | |
SWT.error(SWT.ERROR_IO, e); | |
} | |
} | |
bufferCurrentPosition = 0; | |
} | |
static JPEGSegment seekUnspecifiedMarker(LEDataInputStream byteStream) { | |
byte[] byteArray = new byte[2]; | |
try { | |
while (true) { | |
if (byteStream.read(byteArray, 0, 1) != 1) return null; | |
if (byteArray[0] == (byte) 0xFF) { | |
if (byteStream.read(byteArray, 1, 1) != 1) return null; | |
if (byteArray[1] != (byte) 0xFF && byteArray[1] != 0) { | |
byteStream.unread(byteArray); | |
return new JPEGSegment(byteArray); | |
} | |
} | |
} | |
} catch (IOException e) { | |
SWT.error(SWT.ERROR_IO, e); | |
} | |
return null; | |
} | |
PaletteData setUpPalette() { | |
if (nComponents == 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) != byteArray.length) { | |
throw new SWTError(SWT.ERROR_INVALID_IMAGE); | |
} | |
if (!(byteArray[0] == -1 && byteArray[1] != 0 && byteArray[1] != -1)) { | |
throw new SWTError(SWT.ERROR_INVALID_IMAGE); | |
} | |
int delta = jpegSegment.getSegmentLength() - 2; | |
byteStream.skip(delta); | |
} catch (Exception e) { | |
SWT.error(SWT.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(ImageData image) { | |
if (!new JPEGStartOfImage().writeToStream(outputStream)) { | |
SWT.error(SWT.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)) { | |
SWT.error(SWT.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)) { | |
SWT.error(SWT.ERROR_IO); | |
} | |
if (!chromDQT.writeToStream(outputStream)) { | |
SWT.error(SWT.ERROR_IO); | |
} | |
int frameLength, scanLength, precision; | |
int[][] frameParams, scanParams; | |
if (image.depth == 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)) { | |
SWT.error(SWT.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)) { | |
SWT.error(SWT.ERROR_IO); | |
} | |
JPEGHuffmanTable[] allTables = dhtTable.getAllTables(); | |
for (int j = 0; j < allTables.length; j++) { | |
JPEGHuffmanTable huffmanTable = allTables[j]; | |
if (huffmanTable.getTableClass() == 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)) { | |
SWT.error(SWT.ERROR_IO); | |
} | |
convertImageToYCbCr(image); | |
resetOutputBuffer(); | |
currentByte = 0; | |
currentBitCount = 0; | |
encodeScan(); | |
if (!new JPEGEndOfImage().writeToStream(outputStream)) { | |
SWT.error(SWT.ERROR_IO); | |
} | |
} | |
} |