📄 jpegdecoder.java
字号:
dcTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
huffmanVal);
// Assign AC Huffman Table.
else if (tableClass == HuffmanTable.JPEG_AC_TABLE)
acTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
huffmanVal);
}
break;
case JPEGMarker.DQT:
// DQT non-SOF Marker - This defines the quantization
// coeffecients, this allows us to figure out the quality of
// compression and unencode the data. The data is loaded and
// then stored in to an array.
short quantizationLength = (short) (jpegStream.readShort() - 2);
for (int j = 0; j < quantizationLength / 65; j++)
{
byte quantSpecs = jpegStream.readByte();
int[] quantData = new int[64];
if ((byte) (quantSpecs >> 4) == 0)
// Precision 8 bit.
{
for (int i = 0; i < 64; i++)
quantData[i] = jpegStream.readByte();
}
else if ((byte) (quantSpecs >> 4) == 1)
// Precision 16 bit.
{
for (int i = 0; i < 64; i++)
quantData[i] = jpegStream.readShort();
}
qTables[(int) (quantSpecs & 0x0f)] = new JPEGQTable (quantData);
}
break;
case JPEGMarker.SOS:
// SOS non-SOF Marker - Start Of Scan Marker, this is where the
// actual data is stored in a interlaced or non-interlaced with
// from 1-4 components of color data, if three components most
// likely a YCrCb model, this is a fairly complex process.
// Read in the scan length.
jpegStream.readShort();
// Number of components in the scan.
byte numberOfComponents = jpegStream.readByte();
byte[] componentSelector = new byte[numberOfComponents];
for (int i = 0; i < numberOfComponents; i++)
{
// Component ID, packed byte containing the Id for the
// AC table and DC table.
byte componentID = jpegStream.readByte();
byte tableInfo = jpegStream.readByte();
frame.setHuffmanTables(componentID,
acTables[(byte) (tableInfo >> 4)],
dcTables[(byte) (tableInfo & 0x0f)]);
componentSelector[i] = componentID;
}
byte startSpectralSelection = jpegStream.readByte();
byte endSpectralSelection = jpegStream.readByte();
byte successiveApproximation = jpegStream.readByte();
int mcuIndex = 0;
int mcuTotalIndex = 0;
// This loops through until a MarkerTagFound exception is
// found, if the marker tag is a RST (Restart Marker) it
// simply skips it and moves on this system does not handle
// corrupt data streams very well, it could be improved by
// handling misplaced restart markers.
while (true)
{
try
{
// Loop though capturing MCU, instruct each
// component to read in its necessary count, for
// scaling factors the components automatically
// read in how much they need
for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
{
JPEGComponent comp = (JPEGComponent) frame.components.getComponentByID(componentSelector[compIndex]);
comp.readComponentMCU(jpegStream);
}
mcuIndex++;
mcuTotalIndex++;
}
// We've found a marker, see if the marker is a restart
// marker or just the next marker in the stream. If
// it's the next marker in the stream break out of the
// while loop, if it's just a restart marker skip it
catch (JPEGMarkerFoundException bse)
{
// Handle JPEG Restart Markers, this is where the
// count of MCU's per interval is compared with
// the count actually obtained, if it's short then
// pad on some MCU's ONLY for components that are
// greater than one. Also restart the DC prediction
// to zero.
if (marker == JPEGMarker.RST0
|| marker == JPEGMarker.RST1
|| marker == JPEGMarker.RST2
|| marker == JPEGMarker.RST3
|| marker == JPEGMarker.RST4
|| marker == JPEGMarker.RST5
|| marker == JPEGMarker.RST6
|| marker == JPEGMarker.RST7)
{
for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
{
JPEGComponent comp = (JPEGComponent) frame.components.getComponentByID(componentSelector[compIndex]);
if (compIndex > 1)
comp.padMCU(mcuTotalIndex, resetInterval - mcuIndex);
comp.resetInterval();
}
mcuTotalIndex += (resetInterval - mcuIndex);
mcuIndex = 0;
}
else
{
// We're at the end of our scan, exit out.
break;
}
}
}
break;
case JPEGMarker.DRI:
// DRI - This defines the restart interval, if we have a
// restart interval when we reach our restart modulo calculate
// whether the count of MCU's specified in the restart
// interval have been reached, if they havent then pad with
// whatever MCU was last used, this is supposed to be a form of
// error recovery but it turns out that some JPEG encoders
// purposely cause missing MCU's on repeating MCU's to compress
// data even more (even though it adds an extra layer of
// complexity.. But since when is JPEG easy?
jpegStream.skipBytes(2);
resetInterval = jpegStream.readShort();
break;
case JPEGMarker.COM:
// COM - This is a comment that was inserted into the JPEG, we
// simply skip over the comment because it's really of no
// importance, usually contains a verbal description of the
// application or author who created the JPEG.
jpegStream.skipBytes(jpegStream.readShort() - 2);
break;
case JPEGMarker.DNL:
// DNL - This sets the height of the image. This is the Define
// Number Lines for the image, I'm not sure exactly why we need
// this but, whatever we'll abide.
frame.setScanLines(jpegStream.readShort());
break;
case JPEGMarker.EOI:
// EOI - End of Image, this processes the frames and turns the
// frames into a buffered image.
if (jpegFrames.size() == 0)
{
return;
}
else if (jpegFrames.size() == 1)
{
// Only one frame, JPEG Non-Heirarchial Frame.
DCT myDCT = new DCT();
WritableRaster raster =
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
frame.width,
frame.height,
frame.getComponentCount(),
new Point(0, 0));
// Unencode the data.
for (int i = 0; i < frame.getComponentCount(); i++)
{
JPEGComponent comp =
(JPEGComponent) frame.components.get(i);
comp.setQuantizationTable(qTables[comp.quant_id].getTable());
comp.quantitizeData();
comp.idctData(myDCT);
}
// Scale the image and write the data to the raster.
for (int i = 0; i < frame.getComponentCount(); i++)
{
JPEGComponent comp = (JPEGComponent) frame.components.get(i);
comp.scaleByFactors();
comp.writeData(raster, i);
// Ensure garbage collection.
comp = null;
}
// Grayscale Color Image (1 Component).
if (frame.getComponentCount() == 1)
{
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ComponentColorModel ccm =
new ComponentColorModel(cs, false, false,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
image = new BufferedImage(ccm, raster, false,
new Hashtable());
}
// YCbCr Color Image (3 Components).
else if (frame.getComponentCount() == 3)
{
ComponentColorModel ccm =
new ComponentColorModel(new YCbCr_ColorSpace(), false,
false, Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
image = new BufferedImage(ccm, raster, false,
new Hashtable());
}
// Possibly CMYK or RGBA ?
else
{
throw new JPEGException("Unsupported Color Mode: 4 "
+ "Component Color Mode found.");
}
height = frame.height;
width = frame.width;
}
else
{
//JPEG Heirarchial Frame (progressive or baseline).
throw new JPEGException("Unsupported Codec Type:"
+ " Hierarchial JPEG");
}
break;
case JPEGMarker.SOF1:
// ERROR - If we encounter any of the following marker codes
// error out with a codec exception, progressive, heirarchial,
// differential, arithmetic, lossless JPEG's are not supported.
// This is where enhancements can be made for future versions.
// Thankfully 99% of all JPEG's are baseline DCT.
throw new JPEGException("Unsupported Codec Type: Extended "
+ "Sequential DCT JPEG's Not-Supported");
//case JPEGMarker.SOF2:
// throw new JPEGException("Unsupported Codec Type: Progressive DCT JPEG's Not-Supported");
case JPEGMarker.SOF3:
throw new JPEGException("Unsupported Codec Type:"
+ " Lossless (sequential)");
case JPEGMarker.SOF5:
throw new JPEGException("Unsupported Codec Type:"
+ " Differential sequential DCT");
case JPEGMarker.SOF6:
throw new JPEGException("Unsupported Codec Type:"
+ " Differential progressive DCT");
case JPEGMarker.SOF7:
throw new JPEGException("Unsupported Codec Type:"
+ " Differential lossless");
case JPEGMarker.SOF9:
case JPEGMarker.SOF10:
case JPEGMarker.SOF11:
case JPEGMarker.SOF13:
case JPEGMarker.SOF14:
case JPEGMarker.SOF15:
throw new JPEGException("Unsupported Codec Type:"
+ " Arithmetic Coding Frame");
default:
// Unknown marker found, ignore it.
}
marker = jpegStream.findNextMarker();
}
}
// If the current marker is APP0, tries to decode a JFIF extension
// and advances the current marker to the next marker in the stream.
private void decodeJFIFExtension() throws IOException
{
if (marker == JPEGMarker.APP0)
{
int length = jpegStream.readShort();
if (length >= JFXX_FIXED_LENGTH)
{
byte[] identifier = new byte[5];
jpegStream.read(identifier);
if (identifier[0] != JPEGMarker.JFIF_J
|| identifier[1] != JPEGMarker.JFIF_F
|| identifier[2] != JPEGMarker.JFIF_X
|| identifier[3] != JPEGMarker.JFIF_X
|| identifier[4] != JPEGMarker.X00)
// Not a JFXX field. Ignore it and continue.
jpegStream.skipBytes(length - 7);
else
{
byte extension_code = jpegStream.readByte();
switch (extension_code)
{
case JPEGMarker.JFXX_JPEG:
// FIXME: add support for JFIF Extension:
// Thumbnail coded using JPEG.
jpegStream.skipBytes(length - 8);
case JPEGMarker.JFXX_ONE_BPP:
// FIXME: add support for JFIF Extension:
// Thumbnail stored using 1 byte/pixel.
jpegStream.skipBytes(length - 8);
case JPEGMarker.JFXX_THREE_BPP:
// FIXME: add support for JFIF Extension:
// Thumbnail stored using 3 bytes/pixel.
jpegStream.skipBytes(length - 8);
}
}
}
else
{
// Unknown APP0 marker. Ignore it and continue.
jpegStream.skipBytes(length - 2);
}
marker = jpegStream.findNextMarker();
}
}
public BufferedImage getImage()
{
return image;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -