1:
37:
38: package ;
39:
40: import ;
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49:
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61:
62: public class JPEGDecoder
63: {
64: byte majorVersion;
65: byte minorVersion;
66: byte units;
67: short Xdensity;
68: short Ydensity;
69: byte Xthumbnail;
70: byte Ythumbnail;
71: byte[] thumbnail;
72: BufferedImage image;
73: int width;
74: int height;
75:
76: byte marker;
77:
78:
81: public static final byte MAJOR_VERSION = (byte) 1;
82: public static final byte MINOR_VERSION = (byte) 2;
83:
84:
87: public static final short JFIF_FIXED_LENGTH = 16;
88:
89:
93: public static final short JFXX_FIXED_LENGTH = 8;
94:
95: private JPEGImageInputStream jpegStream;
96:
97: ArrayList jpegFrames = new ArrayList();
98:
99: JPEGHuffmanTable[] dcTables = new JPEGHuffmanTable[4];
100: JPEGHuffmanTable[] acTables = new JPEGHuffmanTable[4];
101: JPEGQTable[] qTables = new JPEGQTable[4];
102:
103: public int getHeight()
104: {
105: return height;
106: }
107:
108: public int getWidth()
109: {
110: return width;
111: }
112: public JPEGDecoder(ImageInputStream in)
113: throws IOException, JPEGException
114: {
115: jpegStream = new JPEGImageInputStream(in);
116: jpegStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
117:
118: if (jpegStream.findNextMarker() != JPEGMarker.SOI)
119: throw new JPEGException("Failed to find SOI marker.");
120:
121: if (jpegStream.findNextMarker() != JPEGMarker.APP0)
122: throw new JPEGException("Failed to find APP0 marker.");
123:
124: int length = jpegStream.readShort();
125: if (!(length >= JFIF_FIXED_LENGTH))
126: throw new JPEGException("Failed to find JFIF field.");
127:
128: byte[] identifier = new byte[5];
129: jpegStream.read(identifier);
130: if (identifier[0] != JPEGMarker.JFIF_J
131: || identifier[1] != JPEGMarker.JFIF_F
132: || identifier[2] != JPEGMarker.JFIF_I
133: || identifier[3] != JPEGMarker.JFIF_F
134: || identifier[4] != JPEGMarker.X00)
135: throw new JPEGException("Failed to read JFIF identifier.");
136:
137: majorVersion = jpegStream.readByte();
138: minorVersion = jpegStream.readByte();
139: if (majorVersion != MAJOR_VERSION
140: || (majorVersion == MAJOR_VERSION
141: && minorVersion < MINOR_VERSION))
142: throw new JPEGException("Unsupported JFIF version.");
143:
144: units = jpegStream.readByte();
145: if (units > (byte) 2)
146: throw new JPEGException("Units field is out of range.");
147:
148: Xdensity = jpegStream.readShort();
149: Ydensity = jpegStream.readShort();
150: Xthumbnail = jpegStream.readByte();
151: Ythumbnail = jpegStream.readByte();
152:
153:
154: int thumbnailLength = 3 * Xthumbnail * Ythumbnail;
155: if (length > JFIF_FIXED_LENGTH
156: && thumbnailLength != length - JFIF_FIXED_LENGTH)
157: throw new JPEGException("Invalid length, Xthumbnail"
158: + " or Ythumbnail field.");
159:
160: if (thumbnailLength > 0)
161: {
162: thumbnail = new byte[thumbnailLength];
163: if (jpegStream.read(thumbnail) != thumbnailLength)
164: throw new IOException("Failed to read thumbnail.");
165: }
166: }
167:
168: public void decode()
169: throws IOException
170: {
171: System.out.println ("DECODE!!!");
172:
173:
174:
175: JPEGFrame frame = null;
176:
177:
178:
179:
180:
181:
182: int resetInterval = 0;
183:
184:
185:
186:
187:
188: byte marker = jpegStream.findNextMarker();
189:
190:
191:
192:
193: decodeJFIFExtension();
194:
195:
196:
197:
198: while (true)
199: {
200: switch (marker)
201: {
202:
203:
204: case JPEGMarker.APP0:
205: case JPEGMarker.APP1:
206: case JPEGMarker.APP2:
207: case JPEGMarker.APP3:
208: case JPEGMarker.APP4:
209: case JPEGMarker.APP5:
210: case JPEGMarker.APP6:
211: case JPEGMarker.APP7:
212: case JPEGMarker.APP8:
213: case JPEGMarker.APP9:
214: case JPEGMarker.APP10:
215: case JPEGMarker.APP11:
216: case JPEGMarker.APP12:
217: case JPEGMarker.APP13:
218: case JPEGMarker.APP14:
219: case JPEGMarker.APP15:
220: jpegStream.skipBytes(jpegStream.readShort() - 2);
221: break;
222:
223: case JPEGMarker.SOF0:
224:
225:
226:
227:
228:
229: jpegFrames.add(new JPEGFrame());
230: frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
231:
232: jpegStream.readShort();
233:
234: frame.setPrecision(jpegStream.readByte());
235:
236: frame.setScanLines(jpegStream.readShort());
237:
238: frame.setSamplesPerLine(jpegStream.readShort());
239:
240: frame.setComponentCount(jpegStream.readByte());
241:
242:
243:
244: if (frame.getComponentCount() == 1)
245: frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
246: else
247: frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
248:
249: for (int i = 0; i < frame.getComponentCount(); i++)
250: frame.addComponent(jpegStream.readByte(), jpegStream.readByte(),
251: jpegStream.readByte());
252: break;
253:
254: case JPEGMarker.SOF2:
255: jpegFrames.add(new JPEGFrame());
256: frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
257:
258: jpegStream.readShort();
259:
260: frame.setPrecision(jpegStream.readByte());
261:
262: frame.setScanLines(jpegStream.readShort());
263:
264: frame.setSamplesPerLine(jpegStream.readShort());
265:
266: frame.setComponentCount(jpegStream.readByte());
267:
268:
269:
270: if (frame.getComponentCount() == 1)
271: frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
272: else
273: frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
274:
275:
276: for (int i = 0; i < frame.getComponentCount(); i++)
277: frame.addComponent(jpegStream.readByte(), jpegStream.readByte(),
278: jpegStream.readByte());
279: break;
280:
281: case JPEGMarker.DHT:
282:
283:
284:
285:
286:
287:
288:
289: int huffmanLength = (jpegStream.readShort() - 2);
290:
291:
292: int index = huffmanLength;
293:
294:
295:
296:
297: while (index > 0)
298: {
299:
300:
301:
302:
303: byte huffmanInfo = jpegStream.readByte();
304: byte tableClass = (byte) (huffmanInfo >> 4);
305: byte huffmanIndex = (byte) (huffmanInfo & 0x0f);
306: short[] codeLength = new short[16];
307: jpegStream.readFully(codeLength, 0, codeLength.length);
308: int huffmanValueLen = 0;
309: for (int i = 0; i < 16; i++)
310: huffmanValueLen += codeLength[i];
311: index -= (huffmanValueLen + 17);
312: short[] huffmanVal = new short[huffmanValueLen];
313: for (int i = 0; i < huffmanVal.length; i++)
314: huffmanVal[i] = jpegStream.readByte();
315:
316: if (tableClass == HuffmanTable.JPEG_DC_TABLE)
317: dcTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
318: huffmanVal);
319:
320: else if (tableClass == HuffmanTable.JPEG_AC_TABLE)
321: acTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
322: huffmanVal);
323: }
324: break;
325: case JPEGMarker.DQT:
326:
327:
328:
329:
330: short quantizationLength = (short) (jpegStream.readShort() - 2);
331: for (int j = 0; j < quantizationLength / 65; j++)
332: {
333: byte quantSpecs = jpegStream.readByte();
334: int[] quantData = new int[64];
335: if ((byte) (quantSpecs >> 4) == 0)
336:
337: {
338: for (int i = 0; i < 64; i++)
339: quantData[i] = jpegStream.readByte();
340:
341: }
342: else if ((byte) (quantSpecs >> 4) == 1)
343:
344: {
345: for (int i = 0; i < 64; i++)
346: quantData[i] = jpegStream.readShort();
347: }
348: qTables[(int) (quantSpecs & 0x0f)] = new JPEGQTable (quantData);
349: }
350: break;
351: case JPEGMarker.SOS:
352:
353:
354:
355:
356:
357:
358: jpegStream.readShort();
359:
360: byte numberOfComponents = jpegStream.readByte();
361: byte[] componentSelector = new byte[numberOfComponents];
362: for (int i = 0; i < numberOfComponents; i++)
363: {
364:
365:
366: byte componentID = jpegStream.readByte();
367: byte tableInfo = jpegStream.readByte();
368: frame.setHuffmanTables(componentID,
369: acTables[(byte) (tableInfo >> 4)],
370: dcTables[(byte) (tableInfo & 0x0f)]);
371: componentSelector[i] = componentID;
372: }
373: byte startSpectralSelection = jpegStream.readByte();
374: byte endSpectralSelection = jpegStream.readByte();
375: byte successiveApproximation = jpegStream.readByte();
376:
377: int mcuIndex = 0;
378: int mcuTotalIndex = 0;
379:
380:
381:
382:
383:
384: while (true)
385: {
386: try
387: {
388:
389:
390:
391:
392: for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
393: {
394: JPEGComponent comp = (JPEGComponent) frame.components.getComponentByID(componentSelector[compIndex]);
395: comp.readComponentMCU(jpegStream);
396: }
397: mcuIndex++;
398: mcuTotalIndex++;
399: }
400:
401:
402:
403:
404: catch (JPEGMarkerFoundException bse)
405: {
406:
407:
408:
409:
410:
411:
412: if (marker == JPEGMarker.RST0
413: || marker == JPEGMarker.RST1
414: || marker == JPEGMarker.RST2
415: || marker == JPEGMarker.RST3
416: || marker == JPEGMarker.RST4
417: || marker == JPEGMarker.RST5
418: || marker == JPEGMarker.RST6
419: || marker == JPEGMarker.RST7)
420: {
421: for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
422: {
423: JPEGComponent comp = (JPEGComponent) frame.components.getComponentByID(componentSelector[compIndex]);
424: if (compIndex > 1)
425: comp.padMCU(mcuTotalIndex, resetInterval - mcuIndex);
426: comp.resetInterval();
427: }
428: mcuTotalIndex += (resetInterval - mcuIndex);
429: mcuIndex = 0;
430: }
431: else
432: {
433:
434: break;
435: }
436: }
437: }
438: break;
439: case JPEGMarker.DRI:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449: jpegStream.skipBytes(2);
450: resetInterval = jpegStream.readShort();
451: break;
452: case JPEGMarker.COM:
453:
454:
455:
456:
457: jpegStream.skipBytes(jpegStream.readShort() - 2);
458: break;
459: case JPEGMarker.DNL:
460:
461:
462:
463: frame.setScanLines(jpegStream.readShort());
464: break;
465: case JPEGMarker.EOI:
466:
467:
468:
469: if (jpegFrames.size() == 0)
470: {
471: return;
472: }
473: else if (jpegFrames.size() == 1)
474: {
475:
476:
477: DCT myDCT = new DCT();
478: WritableRaster raster =
479: Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
480: frame.width,
481: frame.height,
482: frame.getComponentCount(),
483: new Point(0, 0));
484:
485:
486: for (int i = 0; i < frame.getComponentCount(); i++)
487: {
488: JPEGComponent comp =
489: (JPEGComponent) frame.components.get(i);
490: comp.setQuantizationTable(qTables[comp.quant_id].getTable());
491: comp.quantitizeData();
492: comp.idctData(myDCT);
493: }
494:
495: for (int i = 0; i < frame.getComponentCount(); i++)
496: {
497: JPEGComponent comp = (JPEGComponent) frame.components.get(i);
498: comp.scaleByFactors();
499: comp.writeData(raster, i);
500:
501: comp = null;
502: }
503:
504: if (frame.getComponentCount() == 1)
505: {
506: ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
507: ComponentColorModel ccm =
508: new ComponentColorModel(cs, false, false,
509: Transparency.OPAQUE,
510: DataBuffer.TYPE_BYTE);
511: image = new BufferedImage(ccm, raster, false,
512: new Hashtable());
513: }
514:
515: else if (frame.getComponentCount() == 3)
516: {
517: ComponentColorModel ccm =
518: new ComponentColorModel(new YCbCr_ColorSpace(), false,
519: false, Transparency.OPAQUE,
520: DataBuffer.TYPE_BYTE);
521: image = new BufferedImage(ccm, raster, false,
522: new Hashtable());
523: }
524:
525: else
526: {
527: throw new JPEGException("Unsupported Color Mode: 4 "
528: + "Component Color Mode found.");
529: }
530: height = frame.height;
531: width = frame.width;
532: }
533: else
534: {
535:
536: throw new JPEGException("Unsupported Codec Type:"
537: + " Hierarchial JPEG");
538: }
539: break;
540: case JPEGMarker.SOF1:
541:
542:
543:
544:
545:
546: throw new JPEGException("Unsupported Codec Type: Extended "
547: + "Sequential DCT JPEG's Not-Supported");
548:
549:
550: case JPEGMarker.SOF3:
551: throw new JPEGException("Unsupported Codec Type:"
552: + " Lossless (sequential)");
553: case JPEGMarker.SOF5:
554: throw new JPEGException("Unsupported Codec Type:"
555: + " Differential sequential DCT");
556: case JPEGMarker.SOF6:
557: throw new JPEGException("Unsupported Codec Type:"
558: + " Differential progressive DCT");
559: case JPEGMarker.SOF7:
560: throw new JPEGException("Unsupported Codec Type:"
561: + " Differential lossless");
562: case JPEGMarker.SOF9:
563: case JPEGMarker.SOF10:
564: case JPEGMarker.SOF11:
565: case JPEGMarker.SOF13:
566: case JPEGMarker.SOF14:
567: case JPEGMarker.SOF15:
568: throw new JPEGException("Unsupported Codec Type:"
569: + " Arithmetic Coding Frame");
570: default:
571:
572: }
573: marker = jpegStream.findNextMarker();
574: }
575: }
576:
577:
578:
579: private void decodeJFIFExtension() throws IOException
580: {
581: if (marker == JPEGMarker.APP0)
582: {
583: int length = jpegStream.readShort();
584:
585: if (length >= JFXX_FIXED_LENGTH)
586: {
587: byte[] identifier = new byte[5];
588: jpegStream.read(identifier);
589: if (identifier[0] != JPEGMarker.JFIF_J
590: || identifier[1] != JPEGMarker.JFIF_F
591: || identifier[2] != JPEGMarker.JFIF_X
592: || identifier[3] != JPEGMarker.JFIF_X
593: || identifier[4] != JPEGMarker.X00)
594:
595: jpegStream.skipBytes(length - 7);
596: else
597: {
598: byte extension_code = jpegStream.readByte();
599:
600: switch (extension_code)
601: {
602: case JPEGMarker.JFXX_JPEG:
603:
604:
605: jpegStream.skipBytes(length - 8);
606: case JPEGMarker.JFXX_ONE_BPP:
607:
608:
609: jpegStream.skipBytes(length - 8);
610: case JPEGMarker.JFXX_THREE_BPP:
611:
612:
613: jpegStream.skipBytes(length - 8);
614: }
615: }
616: }
617: else
618: {
619:
620: jpegStream.skipBytes(length - 2);
621: }
622: marker = jpegStream.findNextMarker();
623: }
624: }
625:
626: public BufferedImage getImage()
627: {
628: return image;
629: }
630: }