in src/main/java/org/apache/commons/imaging/formats/jpeg/decoder/JpegDecoder.java [416:547]
public void visitSos(final int marker, final byte[] markerBytes, final byte[] imageData) {
try (ByteArrayInputStream is = new ByteArrayInputStream(imageData)) {
// read the scan header
final int segmentLength = read2Bytes("segmentLength", is,"Not a Valid JPEG File", getByteOrder());
final byte[] sosSegmentBytes = readBytes("SosSegment", is, segmentLength - 2, "Not a Valid JPEG File");
sosSegment = new SosSegment(marker, sosSegmentBytes);
// read the payload of the scan, this is the remainder of image data after the header
// the payload contains the entropy-encoded segments (or ECS) divided by RST markers
// or only one ECS if the entropy-encoded data is not divided by RST markers
// length of payload = length of image data - length of data already read
final int[] scanPayload = Allocator.intArray(imageData.length - segmentLength);
int payloadReadCount = 0;
while (payloadReadCount < scanPayload.length) {
scanPayload[payloadReadCount] = is.read();
payloadReadCount++;
}
int hMax = 0;
int vMax = 0;
for (int i = 0; i < sofnSegment.numberOfComponents; i++) {
hMax = Math.max(hMax,
sofnSegment.getComponents(i).horizontalSamplingFactor);
vMax = Math.max(vMax,
sofnSegment.getComponents(i).verticalSamplingFactor);
}
final int hSize = 8 * hMax;
final int vSize = 8 * vMax;
final int xMCUs = (sofnSegment.width + hSize - 1) / hSize;
final int yMCUs = (sofnSegment.height + vSize - 1) / vSize;
final Block[] mcu = allocateMcuMemory();
final Block[] scaledMCU = Allocator.array(mcu.length, Block[]::new, Block.SHALLOW_SIZE);
Arrays.setAll(scaledMCU, i -> new Block(hSize, vSize));
final int[] preds = Allocator.intArray(sofnSegment.numberOfComponents);
ColorModel colorModel;
WritableRaster raster;
Allocator.check(Integer.BYTES * sofnSegment.width * sofnSegment.height);
switch (sofnSegment.numberOfComponents) {
case 4:
colorModel = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
final int[] bandMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff };
raster = Raster.createPackedRaster(DataBuffer.TYPE_INT, sofnSegment.width, sofnSegment.height, bandMasks, null);
break;
case 3:
colorModel = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
raster = Raster.createPackedRaster(DataBuffer.TYPE_INT, sofnSegment.width, sofnSegment.height, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff },
null);
break;
case 1:
colorModel = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
raster = Raster.createPackedRaster(DataBuffer.TYPE_INT, sofnSegment.width, sofnSegment.height, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff },
null);
// FIXME: why do images come out too bright with CS_GRAY?
// colorModel = new ComponentColorModel(
// ColorSpace.getInstance(ColorSpace.CS_GRAY), false, true,
// Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
// raster = colorModel.createCompatibleWritableRaster(
// sofnSegment.width, sofnSegment.height);
break;
default:
throw new ImagingException(sofnSegment.numberOfComponents + " components are invalid or unsupported");
}
final DataBuffer dataBuffer = raster.getDataBuffer();
final JpegInputStream[] bitInputStreams = splitByRstMarkers(scanPayload);
int bitInputStreamCount = 0;
JpegInputStream bitInputStream = bitInputStreams[0];
for (int y1 = 0; y1 < vSize * yMCUs; y1 += vSize) {
for (int x1 = 0; x1 < hSize * xMCUs; x1 += hSize) {
// Provide the next interval if an interval is read until it's end
// as long there are unread intervals available
if (!bitInputStream.hasNext()) {
bitInputStreamCount++;
if (bitInputStreamCount < bitInputStreams.length) {
bitInputStream = bitInputStreams[bitInputStreamCount];
}
}
readMcu(bitInputStream, preds, mcu);
rescaleMcu(mcu, hSize, vSize, scaledMCU);
int srcRowOffset = 0;
int dstRowOffset = y1 * sofnSegment.width + x1;
for (int y2 = 0; y2 < vSize && y1 + y2 < sofnSegment.height; y2++) {
for (int x2 = 0; x2 < hSize
&& x1 + x2 < sofnSegment.width; x2++) {
if (scaledMCU.length == 4) {
final int c = scaledMCU[0].samples[srcRowOffset + x2];
final int m = scaledMCU[1].samples[srcRowOffset + x2];
final int y = scaledMCU[2].samples[srcRowOffset + x2];
final int k = scaledMCU[3].samples[srcRowOffset + x2];
final int rgb = ColorConversions.convertCmykToRgb(c, m, y, k);
dataBuffer.setElem(dstRowOffset + x2, rgb);
} else if (scaledMCU.length == 3) {
final int y = scaledMCU[0].samples[srcRowOffset + x2];
final int cb = scaledMCU[1].samples[srcRowOffset + x2];
final int cr = scaledMCU[2].samples[srcRowOffset + x2];
final int rgb = YCbCrConverter.convertYCbCrToRgb(y,
cb, cr);
dataBuffer.setElem(dstRowOffset + x2, rgb);
} else if (mcu.length == 1) {
final int y = scaledMCU[0].samples[srcRowOffset + x2];
dataBuffer.setElem(dstRowOffset + x2, (y << 16)
| (y << 8) | y);
} else {
throw new ImagingException(
"Unsupported JPEG with " + mcu.length
+ " components");
}
}
srcRowOffset += hSize;
dstRowOffset += sofnSegment.width;
}
}
}
image = new BufferedImage(colorModel, raster,
colorModel.isAlphaPremultiplied(), new Properties());
// byte[] remainder = super.getStreamBytes(is);
// for (int i = 0; i < remainder.length; i++)
// {
// System.out.println("" + i + " = " +
// Integer.toHexString(remainder[i]));
// }
} catch (final ImagingException imageReadEx) {
imageReadException = imageReadEx;
} catch (final IOException ioEx) {
ioException = ioEx;
} catch (final RuntimeException ex) {
// Corrupt images can throw NPE and IOOBE
imageReadException = new ImagingException("Error parsing JPEG", ex);
}
}