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