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.plugins.jpeg.JPEGHuffmanTable;
  44: import javax.imageio.plugins.jpeg.JPEGQTable;
  45: import javax.imageio.stream.ImageInputStream;
  46: 
  47: import java.util.ArrayList;
  48: import java.util.Hashtable;
  49: import java.awt.Point;
  50: import java.awt.Transparency;
  51: import java.awt.color.ColorSpace;
  52: import java.awt.image.BufferedImage;
  53: import java.awt.image.ComponentColorModel;
  54: import java.awt.image.DataBuffer;
  55: import java.awt.image.Raster;
  56: import java.awt.image.WritableRaster;
  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:   /**
  75:    * This decoder expects JFIF 1.02 encoding.
  76:    */
  77:   public static final byte MAJOR_VERSION = (byte) 1;
  78:   public static final byte MINOR_VERSION = (byte) 2;
  79: 
  80:   /**
  81:    * The length of the JFIF field not including thumbnail data.
  82:    */
  83:   public static final short JFIF_FIXED_LENGTH = 16;
  84: 
  85:   /**
  86:    * The length of the JFIF extension field not including extension
  87:    * data.
  88:    */
  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:     // 3 * for RGB data
 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:     // The frames in this jpeg are loaded into a list. There is
 169:     // usually just one frame except in heirarchial progression where
 170:     // there are multiple frames.
 171:     JPEGFrame frame = null;
 172: 
 173:     // The restart interval defines how many MCU's we should have
 174:     // between the 8-modulo restart marker. The restart markers allow
 175:     // us to tell whether or not our decoding process is working
 176:     // correctly, also if there is corruption in the image we can
 177:     // recover with these restart intervals. (See RSTm DRI).
 178:     int resetInterval = 0;
 179: 
 180:     // The JPEGDecoder constructor parses the JFIF field.  At this
 181:     // point jpegStream points to the first byte after the JFIF field.
 182: 
 183:     // Find the first marker after the JFIF field.
 184:     byte marker = jpegStream.findNextMarker();
 185: 
 186:     // Check for a JFIF extension field directly following the JFIF
 187:     // header and advance the current marker to the next marker in the
 188:     // stream, if necessary.
 189:     decodeJFIFExtension();
 190: 
 191:     // Loop through until there are no more markers to read in, at
 192:     // that point everything is loaded into the jpegFrames array and
 193:     // can be processed.
 194:     while (true)
 195:       {
 196:         switch (marker)
 197:           {
 198:             // APPn Application Reserved Information - Just throw this
 199:             // information away because we wont be using it.
 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:             // SOFn Start of Frame Marker, Baseline DCT - This is the start
 221:             // of the frame header that defines certain variables that will
 222:             // be carried out through the rest of the encoding. Multiple
 223:             // frames are used in a heirarchiel system, however most JPEG's
 224:             // only contain a single frame.
 225:             jpegFrames.add(new JPEGFrame());
 226:             frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
 227:             // Skip the frame length.
 228:             jpegStream.readShort();
 229:             // Bits percision, either 8 or 12.
 230:             frame.setPrecision(jpegStream.readByte());
 231:             // Scan lines = to the height of the frame.
 232:             frame.setScanLines(jpegStream.readShort());
 233:             // Scan samples per line = to the width of the frame.
 234:             frame.setSamplesPerLine(jpegStream.readShort());
 235:             // Number of Color Components (or channels).
 236:             frame.setComponentCount(jpegStream.readByte());
 237: 
 238:             // Set the color mode for this frame, so far only 2 color
 239:             // modes are supported.
 240:             if (frame.getComponentCount() == 1)
 241:               frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
 242:             else
 243:               frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
 244:             // Add all of the necessary components to the frame.
 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:             // Skip the frame length.
 254:             jpegStream.readShort();
 255:             // Bits percision, either 8 or 12.
 256:             frame.setPrecision(jpegStream.readByte());
 257:             // Scan lines = to the height of the frame.
 258:             frame.setScanLines(jpegStream.readShort());
 259:             // Scan samples per line = to the width of the frame.
 260:             frame.setSamplesPerLine(jpegStream.readShort());
 261:             // Number of Color Components (or channels).
 262:             frame.setComponentCount(jpegStream.readByte());
 263: 
 264:             // Set the color mode for this frame, so far only 2 color
 265:             // modes are supported.
 266:             if (frame.getComponentCount() == 1)
 267:               frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
 268:             else
 269:               frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
 270: 
 271:             // Add all of the necessary components to the frame.
 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:             // DHT non-SOF Marker - Huffman Table is required for decoding
 279:             // the JPEG stream, when we receive a marker we load in first
 280:             // the table length (16 bits), the table class (4 bits), table
 281:             // identifier (4 bits), then we load in 16 bytes and each byte
 282:             // represents the count of bytes to load in for each of the 16
 283:             // bytes. We load this into an array to use later and move on 4
 284:             // huffman tables can only be used in an image.
 285:             int huffmanLength = (jpegStream.readShort() - 2);
 286: 
 287:             // Keep looping until we are out of length.
 288:             int index = huffmanLength;
 289: 
 290:             // Multiple tables may be defined within a DHT marker. This
 291:             // will keep reading until there are no tables left, most
 292:             // of the time there are just one tables.
 293:             while (index > 0)
 294:               {
 295:                 // Read the identifier information and class
 296:                 // information about the Huffman table, then read the
 297:                 // 16 byte codelength in and read in the Huffman values
 298:                 // and put it into table info.
 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:                 // Assign DC Huffman Table.
 312:                 if (tableClass == HuffmanTable.JPEG_DC_TABLE)
 313:                   dcTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
 314:                                                                       huffmanVal);
 315:                 // Assign AC Huffman Table.
 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:             // DQT non-SOF Marker - This defines the quantization
 323:             // coeffecients, this allows us to figure out the quality of
 324:             // compression and unencode the data. The data is loaded and
 325:             // then stored in to an array.
 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:                   // Precision 8 bit.
 333:                   {
 334:                     for (int i = 0; i < 64; i++)
 335:                       quantData[i] = jpegStream.readByte();
 336: 
 337:                   }
 338:                 else if ((byte) (quantSpecs >> 4) == 1)
 339:                   // Precision 16 bit.
 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:             // SOS non-SOF Marker - Start Of Scan Marker, this is where the
 349:             // actual data is stored in a interlaced or non-interlaced with
 350:             // from 1-4 components of color data, if three components most
 351:             // likely a YCrCb model, this is a fairly complex process.
 352: 
 353:             // Read in the scan length.
 354:             jpegStream.readShort();
 355:             // Number of components in the scan.
 356:             byte numberOfComponents = jpegStream.readByte();
 357:             byte[] componentSelector = new byte[numberOfComponents];
 358:             for (int i = 0; i < numberOfComponents; i++)
 359:               {
 360:                 // Component ID, packed byte containing the Id for the
 361:                 // AC table and DC table.
 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:             // This loops through until a MarkerTagFound exception is
 376:             // found, if the marker tag is a RST (Restart Marker) it
 377:             // simply skips it and moves on this system does not handle
 378:             // corrupt data streams very well, it could be improved by
 379:             // handling misplaced restart markers.
 380:             while (true)
 381:               {
 382:                 try
 383:                   {
 384:                     // Loop though capturing MCU, instruct each
 385:                     // component to read in its necessary count, for
 386:                     // scaling factors the components automatically
 387:                     // read in how much they need
 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:                 // We've found a marker, see if the marker is a restart
 397:                 // marker or just the next marker in the stream. If
 398:                 // it's the next marker in the stream break out of the
 399:                 // while loop, if it's just a restart marker skip it
 400:                 catch (JPEGMarkerFoundException bse)
 401:                   {
 402:                     // Handle JPEG Restart Markers, this is where the
 403:                     // count of MCU's per interval is compared with
 404:                     // the count actually obtained, if it's short then
 405:                     // pad on some MCU's ONLY for components that are
 406:                     // greater than one. Also restart the DC prediction
 407:                     // to zero.
 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:                         // We're at the end of our scan, exit out.
 430:                         break;
 431:                       }
 432:                   }
 433:               }
 434:             break;
 435:           case JPEGMarker.DRI:
 436:             // DRI - This defines the restart interval, if we have a
 437:             // restart interval when we reach our restart modulo calculate
 438:             // whether the count of MCU's specified in the restart
 439:             // interval have been reached, if they havent then pad with
 440:             // whatever MCU was last used, this is supposed to be a form of
 441:             // error recovery but it turns out that some JPEG encoders
 442:             // purposely cause missing MCU's on repeating MCU's to compress
 443:             // data even more (even though it adds an extra layer of
 444:             // complexity.. But since when is JPEG easy?
 445:             jpegStream.skipBytes(2);
 446:             resetInterval = jpegStream.readShort();
 447:             break;
 448:           case JPEGMarker.COM:
 449:             // COM - This is a comment that was inserted into the JPEG, we
 450:             // simply skip over the comment because it's really of no
 451:             // importance, usually contains a verbal description of the
 452:             // application or author who created the JPEG.
 453:             jpegStream.skipBytes(jpegStream.readShort() - 2);
 454:             break;
 455:           case JPEGMarker.DNL:
 456:             // DNL - This sets the height of the image. This is the Define
 457:             // Number Lines for the image, I'm not sure exactly why we need
 458:             // this but, whatever we'll abide.
 459:             frame.setScanLines(jpegStream.readShort());
 460:             break;
 461:           case JPEGMarker.EOI:
 462:             // EOI - End of Image, this processes the frames and turns the
 463:             // frames into a buffered image.
 464: 
 465:             if (jpegFrames.size() == 0)
 466:               {
 467:                 return;
 468:               }
 469:             else if (jpegFrames.size() == 1)
 470:               {
 471:                 // Only one frame, JPEG Non-Heirarchial Frame.
 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:                 // Unencode the data.
 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:                 // Scale the image and write the data to the raster.
 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:                     // Ensure garbage collection.
 496:                     comp = null;
 497:                   }
 498:                 // Grayscale Color Image (1 Component).
 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:                 // YCbCr Color Image (3 Components).
 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:                 // Possibly CMYK or RGBA ?
 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:                 //JPEG Heirarchial Frame (progressive or baseline).
 531:                 throw new JPEGException("Unsupported Codec Type:"
 532:                                         + " Hierarchial JPEG");
 533:               }
 534:             break;
 535:           case JPEGMarker.SOF1:
 536:             // ERROR - If we encounter any of the following marker codes
 537:             // error out with a codec exception, progressive, heirarchial,
 538:             // differential, arithmetic, lossless JPEG's are not supported.
 539:             // This is where enhancements can be made for future versions.
 540:             // Thankfully 99% of all JPEG's are baseline DCT.
 541:             throw new JPEGException("Unsupported Codec Type: Extended "
 542:                                     + "Sequential DCT JPEG's Not-Supported");
 543:             //case JPEGMarker.SOF2:
 544:             //  throw new JPEGException("Unsupported Codec Type: Progressive DCT JPEG's Not-Supported");
 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:             // Unknown marker found, ignore it.
 567:           }
 568:         marker = jpegStream.findNextMarker();
 569:       }
 570:   }
 571: 
 572:   // If the current marker is APP0, tries to decode a JFIF extension
 573:   // and advances the current marker to the next marker in the stream.
 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:               // Not a JFXX field.  Ignore it and continue.
 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:                     // FIXME: add support for JFIF Extension:
 599:                     // Thumbnail coded using JPEG.
 600:                     jpegStream.skipBytes(length - 8);
 601:                   case JPEGMarker.JFXX_ONE_BPP:
 602:                     // FIXME: add support for JFIF Extension:
 603:                     // Thumbnail stored using 1 byte/pixel.
 604:                     jpegStream.skipBytes(length - 8);
 605:                   case JPEGMarker.JFXX_THREE_BPP:
 606:                     // FIXME: add support for JFIF Extension:
 607:                     // Thumbnail stored using 3 bytes/pixel.
 608:                     jpegStream.skipBytes(length - 8);
 609:                   }
 610:               }
 611:           }
 612:         else
 613:           {
 614:             // Unknown APP0 marker.  Ignore it and continue.
 615:             jpegStream.skipBytes(length - 2);
 616:           }
 617:         marker = jpegStream.findNextMarker();
 618:       }
 619:   }
 620: 
 621:   public BufferedImage getImage()
 622:   {
 623:     return image;
 624:   }
 625: }