Source for gnu.javax.imageio.jpeg.JPEGDecoder

   1: /* JPEGDecoder.java --
   2:    Copyright (C)  2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.javax.imageio.jpeg;
  39: 
  40: import java.io.IOException;
  41: import java.nio.ByteOrder;
  42: 
  43: import javax.imageio.*;
  44: import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
  45: import javax.imageio.plugins.jpeg.JPEGQTable;
  46: import javax.imageio.spi.*;
  47: import javax.imageio.metadata.*;
  48: import javax.imageio.stream.ImageInputStream;
  49: 
  50: import java.util.ArrayList;
  51: import java.util.Hashtable;
  52: import java.util.Iterator;
  53: import java.awt.Point;
  54: import java.awt.Transparency;
  55: import java.awt.color.ColorSpace;
  56: import java.awt.image.BufferedImage;
  57: import java.awt.image.ComponentColorModel;
  58: import java.awt.image.DataBuffer;
  59: import java.awt.image.Raster;
  60: import java.awt.image.WritableRaster;
  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:   /**
  79:    * This decoder expects JFIF 1.02 encoding.
  80:    */
  81:   public static final byte MAJOR_VERSION = (byte) 1;
  82:   public static final byte MINOR_VERSION = (byte) 2;
  83: 
  84:   /**
  85:    * The length of the JFIF field not including thumbnail data.
  86:    */
  87:   public static final short JFIF_FIXED_LENGTH = 16;
  88: 
  89:   /**
  90:    * The length of the JFIF extension field not including extension
  91:    * data.
  92:    */
  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:     // 3 * for RGB data
 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:     // The frames in this jpeg are loaded into a list. There is
 173:     // usually just one frame except in heirarchial progression where
 174:     // there are multiple frames.
 175:     JPEGFrame frame = null;
 176: 
 177:     // The restart interval defines how many MCU's we should have
 178:     // between the 8-modulo restart marker. The restart markers allow
 179:     // us to tell whether or not our decoding process is working
 180:     // correctly, also if there is corruption in the image we can
 181:     // recover with these restart intervals. (See RSTm DRI).
 182:     int resetInterval = 0;
 183: 
 184:     // The JPEGDecoder constructor parses the JFIF field.  At this
 185:     // point jpegStream points to the first byte after the JFIF field.
 186: 
 187:     // Find the first marker after the JFIF field.
 188:     byte marker = jpegStream.findNextMarker();
 189: 
 190:     // Check for a JFIF extension field directly following the JFIF
 191:     // header and advance the current marker to the next marker in the
 192:     // stream, if necessary.
 193:     decodeJFIFExtension();
 194: 
 195:     // Loop through until there are no more markers to read in, at
 196:     // that point everything is loaded into the jpegFrames array and
 197:     // can be processed.
 198:     while (true)
 199:       {
 200:         switch (marker)
 201:           {
 202:             // APPn Application Reserved Information - Just throw this
 203:             // information away because we wont be using it.
 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:             // SOFn Start of Frame Marker, Baseline DCT - This is the start
 225:             // of the frame header that defines certain variables that will
 226:             // be carried out through the rest of the encoding. Multiple
 227:             // frames are used in a heirarchiel system, however most JPEG's
 228:             // only contain a single frame.
 229:             jpegFrames.add(new JPEGFrame());
 230:             frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
 231:             // Skip the frame length.
 232:             jpegStream.readShort();
 233:             // Bits percision, either 8 or 12.
 234:             frame.setPrecision(jpegStream.readByte());
 235:             // Scan lines = to the height of the frame.
 236:             frame.setScanLines(jpegStream.readShort());
 237:             // Scan samples per line = to the width of the frame.
 238:             frame.setSamplesPerLine(jpegStream.readShort());
 239:             // Number of Color Components (or channels).
 240:             frame.setComponentCount(jpegStream.readByte());
 241: 
 242:             // Set the color mode for this frame, so far only 2 color
 243:             // modes are supported.
 244:             if (frame.getComponentCount() == 1)
 245:               frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
 246:             else
 247:               frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
 248:             // Add all of the necessary components to the frame.
 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:             // Skip the frame length.
 258:             jpegStream.readShort();
 259:             // Bits percision, either 8 or 12.
 260:             frame.setPrecision(jpegStream.readByte());
 261:             // Scan lines = to the height of the frame.
 262:             frame.setScanLines(jpegStream.readShort());
 263:             // Scan samples per line = to the width of the frame.
 264:             frame.setSamplesPerLine(jpegStream.readShort());
 265:             // Number of Color Components (or channels).
 266:             frame.setComponentCount(jpegStream.readByte());
 267: 
 268:             // Set the color mode for this frame, so far only 2 color
 269:             // modes are supported.
 270:             if (frame.getComponentCount() == 1)
 271:               frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
 272:             else
 273:               frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
 274: 
 275:             // Add all of the necessary components to the frame.
 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:             // DHT non-SOF Marker - Huffman Table is required for decoding
 283:             // the JPEG stream, when we receive a marker we load in first
 284:             // the table length (16 bits), the table class (4 bits), table
 285:             // identifier (4 bits), then we load in 16 bytes and each byte
 286:             // represents the count of bytes to load in for each of the 16
 287:             // bytes. We load this into an array to use later and move on 4
 288:             // huffman tables can only be used in an image.
 289:             int huffmanLength = (jpegStream.readShort() - 2);
 290: 
 291:             // Keep looping until we are out of length.
 292:             int index = huffmanLength;
 293: 
 294:             // Multiple tables may be defined within a DHT marker. This
 295:             // will keep reading until there are no tables left, most
 296:             // of the time there are just one tables.
 297:             while (index > 0)
 298:               {
 299:                 // Read the identifier information and class
 300:                 // information about the Huffman table, then read the
 301:                 // 16 byte codelength in and read in the Huffman values
 302:                 // and put it into table info.
 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:                 // Assign DC Huffman Table.
 316:                 if (tableClass == HuffmanTable.JPEG_DC_TABLE)
 317:                   dcTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
 318:                                                                       huffmanVal);
 319:                 // Assign AC Huffman Table.
 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:             // DQT non-SOF Marker - This defines the quantization
 327:             // coeffecients, this allows us to figure out the quality of
 328:             // compression and unencode the data. The data is loaded and
 329:             // then stored in to an array.
 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:                   // Precision 8 bit.
 337:                   {
 338:                     for (int i = 0; i < 64; i++)
 339:                       quantData[i] = jpegStream.readByte();
 340:                   
 341:                   }
 342:                 else if ((byte) (quantSpecs >> 4) == 1)
 343:                   // Precision 16 bit.
 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:             // SOS non-SOF Marker - Start Of Scan Marker, this is where the
 353:             // actual data is stored in a interlaced or non-interlaced with
 354:             // from 1-4 components of color data, if three components most
 355:             // likely a YCrCb model, this is a fairly complex process.
 356: 
 357:             // Read in the scan length.
 358:             jpegStream.readShort();
 359:             // Number of components in the scan.
 360:             byte numberOfComponents = jpegStream.readByte();
 361:             byte[] componentSelector = new byte[numberOfComponents];
 362:             for (int i = 0; i < numberOfComponents; i++)
 363:               {
 364:                 // Component ID, packed byte containing the Id for the
 365:                 // AC table and DC table.
 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:             // This loops through until a MarkerTagFound exception is
 380:             // found, if the marker tag is a RST (Restart Marker) it
 381:             // simply skips it and moves on this system does not handle
 382:             // corrupt data streams very well, it could be improved by
 383:             // handling misplaced restart markers.
 384:             while (true)
 385:               {
 386:                 try
 387:                   {
 388:                     // Loop though capturing MCU, instruct each
 389:                     // component to read in its necessary count, for
 390:                     // scaling factors the components automatically
 391:                     // read in how much they need
 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:                 // We've found a marker, see if the marker is a restart
 401:                 // marker or just the next marker in the stream. If
 402:                 // it's the next marker in the stream break out of the
 403:                 // while loop, if it's just a restart marker skip it
 404:                 catch (JPEGMarkerFoundException bse)
 405:                   {
 406:                     // Handle JPEG Restart Markers, this is where the
 407:                     // count of MCU's per interval is compared with
 408:                     // the count actually obtained, if it's short then
 409:                     // pad on some MCU's ONLY for components that are
 410:                     // greater than one. Also restart the DC prediction
 411:                     // to zero.
 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:                         // We're at the end of our scan, exit out.
 434:                         break;
 435:                       }
 436:                   }
 437:               }
 438:             break;
 439:           case JPEGMarker.DRI:
 440:             // DRI - This defines the restart interval, if we have a
 441:             // restart interval when we reach our restart modulo calculate
 442:             // whether the count of MCU's specified in the restart
 443:             // interval have been reached, if they havent then pad with
 444:             // whatever MCU was last used, this is supposed to be a form of
 445:             // error recovery but it turns out that some JPEG encoders
 446:             // purposely cause missing MCU's on repeating MCU's to compress
 447:             // data even more (even though it adds an extra layer of
 448:             // complexity.. But since when is JPEG easy?
 449:             jpegStream.skipBytes(2);
 450:             resetInterval = jpegStream.readShort();
 451:             break;
 452:           case JPEGMarker.COM:
 453:             // COM - This is a comment that was inserted into the JPEG, we
 454:             // simply skip over the comment because it's really of no
 455:             // importance, usually contains a verbal description of the
 456:             // application or author who created the JPEG.
 457:             jpegStream.skipBytes(jpegStream.readShort() - 2);
 458:             break;
 459:           case JPEGMarker.DNL:
 460:             // DNL - This sets the height of the image. This is the Define
 461:             // Number Lines for the image, I'm not sure exactly why we need
 462:             // this but, whatever we'll abide.
 463:             frame.setScanLines(jpegStream.readShort());
 464:             break;
 465:           case JPEGMarker.EOI:
 466:             // EOI - End of Image, this processes the frames and turns the
 467:             // frames into a buffered image.
 468: 
 469:             if (jpegFrames.size() == 0)
 470:               {
 471:                 return;
 472:               }
 473:             else if (jpegFrames.size() == 1)
 474:               {
 475:                 // Only one frame, JPEG Non-Heirarchial Frame.
 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:                 // Unencode the data.
 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:                 // Scale the image and write the data to the raster.
 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:                     // Ensure garbage collection.
 501:                     comp = null;
 502:                   }
 503:                 // Grayscale Color Image (1 Component).
 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:                 // YCbCr Color Image (3 Components).
 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:                 // Possibly CMYK or RGBA ?
 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:                 //JPEG Heirarchial Frame (progressive or baseline).
 536:                 throw new JPEGException("Unsupported Codec Type:"
 537:                                         + " Hierarchial JPEG");
 538:               }
 539:             break;
 540:           case JPEGMarker.SOF1:
 541:             // ERROR - If we encounter any of the following marker codes
 542:             // error out with a codec exception, progressive, heirarchial,
 543:             // differential, arithmetic, lossless JPEG's are not supported.
 544:             // This is where enhancements can be made for future versions.
 545:             // Thankfully 99% of all JPEG's are baseline DCT.
 546:             throw new JPEGException("Unsupported Codec Type: Extended "
 547:                                     + "Sequential DCT JPEG's Not-Supported");
 548:             //case JPEGMarker.SOF2:
 549:             //    throw new JPEGException("Unsupported Codec Type: Progressive DCT JPEG's Not-Supported");
 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:             // Unknown marker found, ignore it.
 572:           }
 573:         marker = jpegStream.findNextMarker();
 574:       }
 575:   }
 576: 
 577:   // If the current marker is APP0, tries to decode a JFIF extension
 578:   // and advances the current marker to the next marker in the stream.
 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:               // Not a JFXX field.  Ignore it and continue.
 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:                     // FIXME: add support for JFIF Extension:
 604:                     // Thumbnail coded using JPEG.
 605:                     jpegStream.skipBytes(length - 8);
 606:                   case JPEGMarker.JFXX_ONE_BPP:
 607:                     // FIXME: add support for JFIF Extension:
 608:                     // Thumbnail stored using 1 byte/pixel.
 609:                     jpegStream.skipBytes(length - 8);
 610:                   case JPEGMarker.JFXX_THREE_BPP:
 611:                     // FIXME: add support for JFIF Extension:
 612:                     // Thumbnail stored using 3 bytes/pixel.
 613:                     jpegStream.skipBytes(length - 8);
 614:                   }
 615:               }
 616:           }
 617:         else
 618:           {
 619:             // Unknown APP0 marker.  Ignore it and continue.
 620:             jpegStream.skipBytes(length - 2);
 621:           }
 622:         marker = jpegStream.findNextMarker();
 623:       }
 624:   }
 625: 
 626:   public BufferedImage getImage()
 627:   {
 628:     return image;
 629:   }
 630: }